版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、继承性和派生类,1 基类和派生类,可以利用已有的数据类型来定义新的数据类型 所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员 称已存在的用来派生新类的类为基类,又称为父类 由已存在的类派生出的新类称为派生类,又称为子类,单继承、多继承: 继承的结果-扩充,从一个基类派生的继承称为单继承 从多个基类派生的继承称为多继承 单继承 多继承,1.1 派生类的定义格式,单继承的定义格式如下: Class : ; 多继承的定义格式如下: Class : , , ; 常使用如下三种关键字给予表示: public 表示公有继承 private 表示私有继承 protected 表示保护继承,1
2、.2 派生类的三种继承方式,公有继承(public) 基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的 私有继承(private) 基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问 保护继承(protected) 基类的所有公有成员和保护成员都作为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的 系统的默认值是私有继承(private,不同继承方式的基类和派生类特性,类成员 对象 public 对象 protected 对象 private 对象 类型 继承类 继承类 继承类 pr
3、iv X X X X X X X prote X prote X prote X priv X pub V pub V prote X priv X protected : 对派生类的成员来说,是共有的;其余为私有,1.3 基类和派生类的关系,任何一个类都可以派生出一个新类,派生类也可以再派生出新类 类A是类C的间接基类, 类B是类A的直接派生类 基类与派生类之间的关系:可复用的软件构件 派生类是基类的具体化 派生类是基类定义的延续 派生类是基类的组合,2 单继承,每一个类可以有多个派生类 每一个派生类只能有一个基类从而形成树形结构,2.1 成员访问权限的控制,公有继承public 私有继承p
4、rivate 保护继承protected,公有继承(public,公有继承方式创建的派生类对基类各种成员访问权限如下 : 基类公有成员相当于派生类的公有成员,即派生类可以象访问自身公有成员一样访问从基类继承的公有成员。 基类保护成员相当于派生类的保护成员,即派生类可以象访问自身的保护成员一样,访问基类的保护成员。 对于基类的私有成员,派生类内部成员无法直接访问。派生类使用者也无法通过派生类对象直接访问,例 分析程序中的访问权限 #include class A public: void f 1( ); protected: int j1; private: int i1;,class B:pu
5、blic A public: void f2( ); protected: int j2; private: int i2; ; class C:public B public: void f3( );,回答下列问题: 派生类B中成员函数f2( )能否访问基类A中的成员:f1( ), i1 和 j1呢? 派生类B的对象b1能否访问基类A中的成员:f1( ), i1 和 j1呢? 派生类C中成员函数f3( )能否访问直接基类B中的成员:f2( )和j2呢?能否访问间接基类A中的成员:f1( ), i1 和 j1 呢? 派生类C的对象c1能否访问直接基类B中的成员:f2( )和j2呢?能否访问间接
6、基类A中的成员:f1( ), i1和j1呢? 从对(1)(4)问题的回答可得出什么结论,Ans,可以访问f1( )和j1,而不可以访问i1。 可以访问f1( ),而不可以访问j1和i1。 可以访问直接基类中的f2( )和j2以及间接基类中的f1( )和j1,而不可以访问i2和i1。 可以访问直接基类中的f2( )和间接基类中的f1( ),其它的都不可以访问。 在公有继承时,派生类的成员函数可访问基类中的公有成员和保护成员;派生类的对象仅可访问基类中的公有成员,私有继承 (private,派生类对基类各种成员访问权限如下 : 基类公有成员和保护成员都相当于派生类的私有成员,派生类只能通过自身的函
7、数成员访问他们 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问,例分析程序,回答问题,include class A public: void f(int i)cout i endl: void g( ) cout gn; ; class B:A public: void h( ) cout hn; A:f;,void main( ) B d1; d1.f(6); d1.g( ); d1.h( );,回答下列问题: 执行该程序时,哪个语句会出现编译错?为什么? 去掉出错语句后,执行该程序后输出结果如何? 程序中派生类B是从基类A继承来的,这种缺省继承方式是哪种继承方式? 派
8、生类B中,A:f的含义是什么? 将派生类B的继承改为公有继承方式该程序输出什么结果,Ans,1 d1.g( );语句出现编译错误,因为B是以私有继承方式继承类A的,所以B类的对象不可访问A类的成员函数。 2 d1.g( );语句注释后,执行该程序输出以下结果: 6 h 3 使用class关键字定义类时,缺省的继承方式是private。 4 A:f;是将基类中的公有成员说明为派生类的公有成员。 5 将class B:A改为class B:public A以后,输出如下: 6 g h,保护继承(public,保护继承方式创建的派生类对基类各种成员访问权限如下 : 基类的公有成员和保护成员都相当于派
9、生类的保护成员,派生类可以通过自身的成员函数或其子类的成员函数访问他们 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问,例 分析程序,回答问题,Include #include class A public: A(const char *nm)strcpy(name, nm); private: char name80; ; class B:public A public: B(const char *nm):A(nm); void PrintName( )const;,void B:PrintName( ) const cout name name endl; void
10、main( ) B b1(wang li); b1.PrintName( );,回答下列问题,执行该程序将会出现什么编译错? 对出现的编译错如何在访问权限上进行修改? 修改后使该程序通过编译,执行执行该程序后输出结果是什么,Ans,1 编译时出错行是:coutname:nameendl; 错误信息提示name是私有成员不能访问。 2 在类A中,将private改写为protected。这样就可以通过编译。 派生类可访问基类的保护部分,并把它作为派生类的公有部分;但程序其他部分把name作为私有成员。例如在main中,不能运行 strcpy(s1,) 3 执行修改后的该程序输出如下
11、结果: wang li,2.2 构造函数和析构函数,1. 构造函数 派生类对象是由基类中说明的数据成员和派生类中说明的数据成员共同构成 基类中说明的数据成员和操作所构成的封装体称为基类子对象 派生类的构造函数必须通过调用基类的构造函数类初始化基类子对象 在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类的数据成员得以初始化。如果派生类中还有子对象时,还应包含对子对象初始化的构造函数,派生类构造函数,派生类构造函数的格式如下: () : (),() ; 派生类构造函数的调用顺序如下: 基类的构造函数 子对象类的构造函数(如果有的话) 派生类构造函数,例,i
12、nclude class A public: A( )a=0; coutAs default constructor called.n; A(int i)a=i; coutAs constructor called.n; A( )cout As destructor called.n; void Print( ) const cout a ,; int Geta( )return a; private: int a;,class B:public A public: B( )b=0; cout Bs default constructor called.n; B(int i, int j, i
13、nt k); B( )cout Bs destrutor called.n; void Print( ); private: int b; A aa; ; B:B(int i, int j, int k) : A(i),aa(j) b = k; cout Bs constructor called.n;,void B:Print( ) A:Print( ); cout b , aa.Geta( ) endl; void main( ) B bb2; bb0 =B(1,2,5); bb1 =B(3,4,7); for(int i=0; i2; i+) bbi.Print( );,Ans,As d
14、efault constructor called. As default constructor called. 构造函数 Bs default constructor called. As default constructor called. As default constructor called. 构造函数 Bs default constructor called. As constructor called. As constructor called. Bs constructor called. 赋值 Bs destructor called. As destructor
15、called. As destructor called. As constructor called. As constructor called. Bs constructor called. 赋值 Bs destructor called. As destructor called. As destructor called. 1, 5, 2 3, 7, 4 Bs destructor called. As destructor called. As destructor called. Bs destructor called. As destructor called. As des
16、tructor called,派生类的构造函数,再如: class Person char m_strName10; intm_nAge; public: Person(char* name,int age) strcpy(m_strName, name); m_nAge = age; coutconstructor of personm_strNameendl; Person() coutdeconstrutor of personm_strNameendl;,class Employee : public Person char m_strDept20; Person Wang; publ
17、ic: Employee(char *name, int age, char *dept, char *name1, int age1) : Person(name,age) , Wang(name1,age1) strcpy(m_strDept, dept); coutconstructor of Employeeendl; Employee() coutdeconstrucor of Employeeendl;,2. 析构函数,当对象被删除时,派生类的析构函数被执行 由于析构函数不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用 先执行派生类的析构函数,再执行基类的析构函数
18、,例,include class M public: M( )m1 = m2 = 0; M(int i, int j)m1 = i; m2 = j; void print( )cout m1 , m2 ,: M( )cout Ms destructor called.n; private: int m1, m2;,class N:public M public: N( )n = 0; N(int i, int j, int k); void print( )M:print( ); cout n endl; N( )cout Ns destructor called.n; private: in
19、t n; ; N:N(int i, int j, int k) : M(i,j), n(k) void main( ) N n1(5,6,7), n2(-2,-3,-4); n1.print( ); n2.print( );,Ans,5, 6, 7 -2, -3, -4, Ns destructor called. Ms destructor called. Ns destructor called. Ms destructor called,派生类构造函数应注意的问题,派生类构造函数的定义中可以省略对基类构造函数的调用 条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数,1,例,in
20、clude class A public: A( )a = 0; A(int i)a = i; void print( )cout a ,; private: int a;,class B:public A public: B( )b1 = b2 = 0; B(int i)b1 = i; b2 = 0; B(int i,int j,int k) : A(i), b1(j), b2(k) void print( )A:print( ); cout b1 , b2 endl; private: int b1,b2; ; void main( ) B d1; B d2(5); B d3(4,5,6)
21、; / 两个参数,编译出错 d1.print( ); d2.print( ); d3.print( );,Ans,0, 0, 0 0, 5, 0 4, 5, 6,2,当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数途径 在有的情况下,派生类构造函数的函数体可能为空,仅起到参数传递作用,例如,class B public: B(int i, int j)b1 = i; b2 = j; private: int b1, b2; ; class D:public B public: D(int i, int j, int k, int l, int m);
22、 private: int d1; B bb; ; D:D(int i, int j, int k, int l, int m) : B(i, j), bb(k, l) d1 = m;,解释,派生类D的构造函数有5个参数 前两个参数传递给基类B的构造函数 接着两个参数传递给子对象bb的类B的构造函数 最后一个参数是传递给派生类D的数据成员d1,2.3 子类型化和类型适应,1. 子类型化 类型化的概念涉及到行为共享,它与继承有着密切的关系 有一个特定的类型S,当且仅当它至少提供了类型T行为,则称类型S是类型T的子类型 子类型是类型之间一般和特殊的关系,例,class A public: void
23、 Print( ) onst cout A:print( )called.n; ; class B : public A public: void f( ) ; 类 B 公有继承了类 A,类 B 是类 A 的一个子类型 类 B 是类A的子类型,类 B 具备类A中的操作 类 A 中的操作可以被用于操作类 B 的对象,例,void f1(const A 执行该程序将会输出如下结果: A:Print( ) called. 类B的对象b交给了处理类A的对象的函数f1( )进行处理。 对类A的对象操作的函数,可以对类A的子类的对象进行操作。 子类型关系是不可逆的,子类型关系是不对称的,2. 类型适应,类
24、型适应是指两种类型之间的关系 派生类的对象可以用于基类对象所能使用的场合,我们说派生类适应于基类 派生类对象的指针和引用也适应于基类对象的指针和引用 子类型化与类型适应是一致的。A类型是B类型的子类型,那么A类型必将适应于B类型。 子类型的重要性就在于减轻程序人员编写程序代码的负担 一个函数可以用于某类型的对象,则它也可用于该类型的各个子类型的对象 不必为处理这些子类型的对象去重载该函数,例,include class A public: A( )a = 0; A(int i)a = i; void print( )cout a endl; int geta( )return a; priva
25、te: int a; ; class B : public A public; B( )b = 0; B(int i, int j) : A(i), b(j) void print( )A:print( ); cout b endl; private: int b;,void fun(A,Ans,9 1 90 解释: aa = bb; pa = pb; 合法 bb = aa; pb = pa; 非法,3 多继承,3.1 多继承的概念 多继承可以看作是单继承的扩展 派生类与每个基类之间的关系仍可看作是一个单继承 多继承下派生类的定义格式如下: class : , ,多继承中派生类与多个基类之间关
26、系,例如: class A ; class B ; class C : public A, public B ; 派生类C具有两个基类(类A和类B)。 派生类C的成员包含了基类A中成员和基类B中成员以及该类本身的成员,3.2 多继承的构造函数,多继承的派生类的构造函数格式如下: () : (), (),(), ; 必须同时负责该派生类所有基类构造函数的调用 派生类的参数个数必须等于所有基类初始化所需的参数个数 执行顺序时先执行所有基类的构造函数,再执行派生类本身构造函数 处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺
27、序无关,例,include class B1 public: B1(int i); b1 = i; cout constructor B1. i endl; void print( )cout b1 endl; private: int b1;,class B2 public: B2(int i); b2 = i; cout constructor B2. I endl; void print( )cout b2 endl; private: int b2; ; class B3 public: B3(int i); b3 = i; cout condtructor B3. i endl; i
28、nt getb3( )return b3; private: int b3;,class A : public B2, public B1 public: A(int i, int j, int k, int l) : B1(i), B2(j), bb(k) a = l; cout constructor A. l endl; void print( ) B1:print( ); B2:print( ); cout a , bb.getb3( ) endl; private: int a; B3 bb; ; void main( ) A aa(1, 2, 3, 4); aa.print( );
29、,Ans,constructor B2.2 /B2构造函数,B2.b2=j=2 constructor B1.1 /B1构造函数,B1.b1=i=1 constructor B3.3 /生成B3的对象,B3.bb=k=3 constructor A.4 /A构造函数,A.a=l=4 1 /B1:Print 2 /B2:Print 4, 3 /a, bb.getb3(,3.3 二义性问题,由于多继承情况下,可能造成对基类中成员的访问出现了不唯一的情况,则称为对基类成员访问的二义性问题,例如,class A public: void f( ); ; class B public: void f(
30、); void g( ); ; class C : public A,public B public: void g( ); void f( );,解释,如果定义一个类C的对象c1: C c1; 对函数f( )的访问 c1.f( );便有二义性 是访问类A中的f( ),还是访问类B中的f( )呢 解决的方法:用成员名限定法来消除二义性,例如 c1.A:f( ); 或者 c1.B:f( ); 最好的解决方法:在类C中定义一个同名成员f( ), 类C中的f( )再根据需要来决定调用A:f( ),还是B:f( ),还是两者皆有,这样,c1.f( )将调用C:f(,类C中成员函数调用f( )也会出现二
31、义性,void C:h( ) f( ); 该函数应该修改为: void C:h( ) 或者 void C:h( ) 或者 void C:h( ) A:f( ); B : :f( ); A:f( ); B:f( );,派生类的成员将支配基类中的同名成员,类B中有成员函数g( ),类C中也有成员函数g( ) c1.g ( ); 不存在二义性 它是指C:g( ),而不是指B:g( )。 两个g( )函数,一个出现在基类B,一个出现在派生类C,规定派生类的成员将支配基类中的同名成员 DAG的图表示法,例如,class A public: int a; ; class B1 : public A pri
32、vate: int b1; ; class B2 : public A private: int b2; ; class B2 : public A private: int b2; ; class C : public B1, public B2 public: int f( ); private: int c;,当派生类从多个基类派生,这些基类又有一个共同的基类,可能会出现二义性,使用图表示如下: 已知:C c1; 下面的两个访问都有二义性: c1.a; c1.A:a; 而下面两个访问是正确的: c1.B1:a; c1.B2:a; 类C的成员函数f( ) 可以消除二义性: int C:f(
33、 ) return B1:a + B2:a;,例7.9,include class A public: A(int i)a = i; cout con.An; void print( )cout a endl; A( )cout des.An; prinvate: int a;,class B1 : public A public: B1(int i, int j) : A(i)b1 = j; cout con.B1n; void print( )A:print( ); cout b1 endl; B1( )cout des.B1n; private: int b1; ; class B2 :
34、 public A public: B2(int i, int j) : A(i)b2 = j; cout con.B2n; void print( )A:print( ); cout b2 endl; B2( )cout des.B2n; private: int b2;,class C : public B1, public B2 public: C(int i, int j, int k, int l, int m) : B1(i,j), B2(k,l), c(m) cout con.Cn; void print( ) B1:print( ); B2:print( ); cout c e
35、ndl; C( )cout des.Cn; private: int c; ; void main( ) C c1(1,2,3,4,5); c1.print( );,Ans,con.A con.B1 con.A con.B2 con.C 1 2 3 4 5 des.C des.B2 des.A des.B1 des.A,4 虚基类,由于类 A 是派生类 C 两条继承路径上的一个公共基类,此公共基类将在派生类的对象产生多个基类子对象 基类设定虚基类,可只产生一个基类子对象,4.1 虚基类的引入和说明,引进虚基类的真正目的是为了解决二义性问题 虚基类说明格式如下: virtual,例,class A public: void f( ); 图示如下: protected: int a; ; class B : viutual public A protected:
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年全新捐赠三方协议协议
- 2024年个人货车租用合同
- 2024年个人聘用合同格式范本
- 专家法律意见2024年
- 2024年纸箱采购合同模板600字
- 2024年债权、债务转让合同范本
- 2024年房地产转让合同
- 山东(选调生)申论2007年
- 2024年全新物流服务咨询合同
- 2024年机房助管人员劳动合同范本
- 1.天津市装饰装修定额说明、计算规则
- 苗木材料报审及进场清单
- 对公客户管理方案(修改版)
- 肾移植术的解剖(1)
- 《政务礼仪》PPT课件.ppt
- 文身的危害PPT精选课件
- IFRS17保险合同准则评析及影响分析
- PLC课程设计(停车场车位控制 )
- 软件项目运维工作记录表
- 《羊道春牧场》读后感作文5篇
- 铁塔安装施工方案(完整版)
评论
0/150
提交评论