




已阅读5页,还剩73页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
继承性和派生类,继承性是面向对象程序设计中最重要的机制 克服了传统程序设计方法对编写出来的程序无法重复使用 可以扩充和完善旧的程序设计以适应新的需求 为未来程序设计增添了新的资源,1 基类和派生类,可以利用已有的数据类型来定义新的数据类型 所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员 称已存在的用来派生新类的类为基类,又称为父类 由已存在的类派生出的新类称为派生类,又称为子类,单继承、多继承: 继承的结果-扩充,从一个基类派生的继承称为单继承 从多个基类派生的继承称为多继承 单继承 多继承,1.1 派生类的定义格式,单继承的定义格式如下: Class : ; 多继承的定义格式如下: Class : , , ; 常使用如下三种关键字给予表示: public 表示公有继承 private 表示私有继承 protected 表示保护继承,1.2 派生类的三种继承方式,公有继承(public) 基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的 私有继承(private) 基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问 保护继承(protected) 基类的所有公有成员和保护成员都作为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的 系统的默认值是私有继承(private)。,不同继承方式的基类和派生类特性,类成员 对象 public 对象 protected 对象 private 对象 类型 继承类 继承类 继承类 priv 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 私有继承private 保护继承protected,公有继承(public),公有继承方式创建的派生类对基类各种成员访问权限如下 : 基类公有成员相当于派生类的公有成员,即派生类可以象访问自身公有成员一样访问从基类继承的公有成员。 基类保护成员相当于派生类的保护成员,即派生类可以象访问自身的保护成员一样,访问基类的保护成员。 对于基类的私有成员,派生类内部成员无法直接访问。派生类使用者也无法通过派生类对象直接访问。,例 分析程序中的访问权限 #include class A public: void f 1( ); protected: int j1; private: int i1; ;,class B:public 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呢?能否访问间接基类A中的成员:f1( ), i1和j1呢? 从对(1)(4)问题的回答可得出什么结论?,Ans:,可以访问f1( )和j1,而不可以访问i1。 可以访问f1( ),而不可以访问j1和i1。 可以访问直接基类中的f2( )和j2以及间接基类中的f1( )和j1,而不可以访问i2和i1。 可以访问直接基类中的f2( )和间接基类中的f1( ),其它的都不可以访问。 在公有继承时,派生类的成员函数可访问基类中的公有成员和保护成员;派生类的对象仅可访问基类中的公有成员。,私有继承 (private),派生类对基类各种成员访问权限如下 : 基类公有成员和保护成员都相当于派生类的私有成员,派生类只能通过自身的函数成员访问他们 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问。,例分析程序,回答问题,#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继承来的,这种缺省继承方式是哪种继承方式? 派生类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),保护继承方式创建的派生类对基类各种成员访问权限如下 : 基类的公有成员和保护成员都相当于派生类的保护成员,派生类可以通过自身的成员函数或其子类的成员函数访问他们 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问,例 分析程序,回答问题,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 main( ) B b1(“wang li“); b1.PrintName( ); ,回答下列问题:,执行该程序将会出现什么编译错? 对出现的编译错如何在访问权限上进行修改? 修改后使该程序通过编译,执行执行该程序后输出结果是什么?,Ans:,1 编译时出错行是:cout“name:“nameendl; 错误信息提示name是私有成员不能访问。 2 在类A中,将private改写为protected。这样就可以通过编译。 派生类可访问基类的保护部分,并把它作为派生类的公有部分;但程序其他部分把name作为私有成员。例如在main中,不能运行 strcpy(s1,) 3 执行修改后的该程序输出如下结果: wang li,2.2 构造函数和析构函数,1. 构造函数 派生类对象是由基类中说明的数据成员和派生类中说明的数据成员共同构成 基类中说明的数据成员和操作所构成的封装体称为基类子对象 派生类的构造函数必须通过调用基类的构造函数类初始化基类子对象 在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类的数据成员得以初始化。如果派生类中还有子对象时,还应包含对子对象初始化的构造函数。,派生类构造函数,派生类构造函数的格式如下: () : (),() ; 派生类构造函数的调用顺序如下: 基类的构造函数 子对象类的构造函数(如果有的话) 派生类构造函数,例,#include class A public: A( )a=0; cout“As default constructor called.n“; A(int i)a=i; cout“As 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, int 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 default 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 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 destructor called.,派生类的构造函数,再如: class Person char m_strName10; int m_nAge; public: Person(char* name,int age) strcpy(m_strName, name); m_nAge = age; cout“constructor of person“m_strNameendl; Person() cout“deconstrutor of person“m_strNameendl; ;,class Employee : public Person char m_strDept20; Person Wang; public: Employee(char *name, int age, char *dept, char *name1, int age1) : Person(name,age) , Wang(name1,age1) strcpy(m_strDept, dept); cout“constructor of Employee“endl; Employee() cout“deconstrucor of Employee“endl; ;,2. 析构函数,当对象被删除时,派生类的析构函数被执行 由于析构函数不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用 先执行派生类的析构函数,再执行基类的析构函数,例,#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: int 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 ),例,#include 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); / 两个参数,编译出错 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); 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 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. 类型适应,类型适应是指两种类型之间的关系 派生类的对象可以用于基类对象所能使用的场合,我们说派生类适应于基类 派生类对象的指针和引用也适应于基类对象的指针和引用 子类型化与类型适应是一致的。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; private: 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 : , , ;,多继承中派生类与多个基类之间关系,例如: class A ; class B ; class C : public A, public B ; 派生类C具有两个基类(类A和类B)。 派生类C的成员包含了基类A中成员和基类B中成员以及该类本身的成员。,3.2 多继承的构造函数,多继承的派生类的构造函数格式如下: () : (), (),(), ; 必须同时负责该派生类所有基类构造函数的调用 派生类的参数个数必须等于所有基类初始化所需的参数个数 执行顺序时先执行所有基类的构造函数,再执行派生类本身构造函数 处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序无关,例,#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; int 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( ); ,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( ); 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( )也会出现二义性,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 private: 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( ) 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 : 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 endl; 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 p
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 全面提升行政管理能力的考试试题及答案
- 2025年度员工健康检查合同协议
- 课题申报书的本质
- 微生物感染传播途径的识别试题及答案
- 课题申报书怎么整页
- 项目闭环管理的必要性试题及答案
- 2025年证券从业资格证考试加速提升技巧试题及答案
- 解析2025年证券从业资格证考试重难点试题及答案
- 新课改瘦专用2025版高考地理一轮复习第二部分人文地理第五章交通运输布局及其影响第二讲交通运输方式和布局变化的影响学案含解析
- 2025年证券从业资格证科学发展试题及答案
- 2025年江苏省徐州市铜山区中考一模道德与法治试题(原卷版+解析版)
- 制造业自检自控流程优化计划
- 《人工智能的进展》课件
- 风湿免疫病患者结核病诊治及预防实践指南(2025版)解读课件
- 大建安-大连市建筑工程安全档案编制指南
- 上海2024-2025学年五年级数学第二学期期末联考模拟试题含答案
- GB/T 45421-2025城市公共设施非物流用智能储物柜服务规范
- 北京市丰台区2025届高三一模试卷语文试题(含答案)
- 安徽省合肥市高三下学期第二次教学质量检测数学试卷(含答案)
- 2025年河南工业贸易职业学院单招职业倾向性测试题库往年题考
- 2025年儿科护理工作计划
评论
0/150
提交评论