C++类的继承与派生
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
总结
继承性是面向对象的一个非常重要的特点,它使得代 码复用性得到最大的体现,使得程序的扩充性得到了 很大的提高。 那么继承性体现的一个原则就是:子类继承父类所有 的内容,但是如果子类中的数据成员和函数成员重名, 则形成override,那么只能通过父类::作用域的形式进 行调用。 在继承中要特别注意:默认构造函数、拷贝构造函数、 =运算符重载、析构函数的使用。 另外还要注意存取权限,体现在两个方面:
保护构造函数与私有构造函数
如果类的构造函数是私有的,这样的类也是抽象类,但是因为派生类也 无法调用它,从而无法用派生的方式初始化,但是可以用静态函数的方 式实例化该类的对象。 class CAbstract { private: CAbstract(){cout<<"test"<<endl;}//保护类型的构造函数 //... public: static CAbstract CreateInstance() { return CAbstract(); } }; void main() { CAbstract pObj=CAbstract::CreateInstance(); }
类的继承方式
派生类并非把基类的所有特性都继承下来。 派生类并非把基类的所有特性都继承下来。它还 受两方面约束: 1. 不论何种方式 , 下面这些基类的特征是不能 不论何种方式, 从基类继承下来的: 构造函数 析构函数 友员关系
类的继承方式
对基类的除( 以外的其他成员的继承受继承方式限制, 对基类的除(1)以外的其他成员的继承受继承方式限制,有三种继 承的方式:私有继承 、 保护继承和 公有继承。 承的方式: 私有继承、 保护继承 和 公有继承 。 不同的继承方式 将导致从基类继承来的成员在派生类中具有不同的访问限制属 性。三种继承方式一般形式为: class 派生类名:private 基类名 派生类名:private //私有继承 //私有继承 { 派生类新增成员 }; 2. class 派生类名:protected 基类名 派生类名:protected { 派生类新增成员 }; class 派生类名:public 基类名 派生类名:public { 派生类新增成员 }; //保护继承 //保护继承
对象间的转换与赋值(派生类与基类间的转换 ) 正如整型数据可以自动转换成double 型一样, 正如整型数据可以自动转换成double型一样 , 基 类对象与派生类对象间也存在赋值兼容的关系。 类对象与派生类对象间也存在赋值兼容的关系。 具体表现在一下几个方面: class CBase { //... //... }; class CDerived:public CBase CDerived: { //... //... };
派生类与基类间的转换
派生类对象可以向基类对象赋值; CBase base; CDerived derived; base=derived; //将派生类对象的基类 部分拷贝给基类对象 派生类对象可以替代基类对象向基类对象的引 用进行赋值或者初始化; 用进行赋值或者初始化;
CBase b1; CDerived d1; CBase &b1Alias=b1; //普通的引用 b1Alias=d1; //将d1的基类部分拷贝给b1Alias(即b1) CBase &b2=d1; //d1基类部分的引用 注意:基类与子类之间无需定义显性运算符重载
多重继承
如果一个派生类同时有两个或者多个基类,派生 类从两个和多个基类中继承所需的属性,这 种 继 承 方 式 称 为 多 重 继 承 (Multiple Inheritance)。 声明多重继承的一般形式为: class 派生类名:继承方式1 基类名1,继承方式2 基类名2,,,继承方式n 基类名n { //... 派生类新增的成员。 }; 例: 见备注中的代码
保护构造函数与私有构造函数
如果一个类的构造函数和析构函数是保护类型的,则不能在程序中实例 化该类的对象,这样的类称为抽象类。由于保护的构造函数可以被派生 类访问,所以我们通常将抽象类用于各种派生类的公共基类,使得该抽 象类派生的类拥有共同的特性。 class CAbstract { protected: CAbstract(){} // //保护类型的构造函数 //... }; class CDerived:public CAbstract { //... }; void main() { //CAbstract objAbs; //错误,无法实例化 CDerived objDer; //可以 }
//将derived的基类部分拷贝给形参base //将derived的基类部分当作形参使用
派生类对象的地址可以赋给基类类型的指针变量,或者说,基类型的指 针可以指向派生类对象。 CBase *pBase; CDerived derived; pBase=&derived;
派生类与派生类间的赋值(拷贝) 派生类与派生类间的赋值(拷贝)
多重继承
数据成员在内存中的布局形式为:
pa
pb
int m_i [CBaseA中的成员] int m_i [CBaseB中的成员] int m_j [CDerived]
调用时必须注意防止二义性: CDerived d; d.m_j=0; //i=d.m_i; d.CBaseA::m_i=1; d.CBaseB::m_i=2; //d.Func(); d.CBaseA::Func(); d.CBaseB::Func(); CBaseA *pa; CBaseB *pb; pa=&d; pb=&d;
派生类对基类成员的覆盖
如果基类和派生类中存在同名的 成员数据或者成员函数,那么派 生类的成员数据和成员函数将覆 盖掉基类的成员数据和成员函数。 而且与基类和派生类的访问属性 无关;与基类和派生类的函数间 的参数和返回类型无关。 见备注中的代码
派生类对象的初始化与清除
如果需要在初始化派生类成员时也初始化基类 的成员,则可在派生类的构造函数的初始化成 员列表中初始化基类,如果基类的构造函数均 带有参数,则基类必须出现在派生类的初始化 成员列表中。 派生类构造函数及初始化列表的一般形式为: 派生类名(构造函数参数列表): 基类名(参数列 表),派生类成员1(参数列表1),,,派生类成员 n(参数列表n) 例:(见备注中的代码)
CBase
CBaseA
这样派生类对象可以直接使用公 共基类的成员: CDerived d; d.m_i=9;
CBaseB
CDerived
课堂练习
通过下面的4个例子温习上面的内容: 通过下面的 个例子温习上面的内容: 个例子温习上面的内容 Concept.cpp Construct.cpp Convert.cpp overx.cpp
//公有继承 //公有继承
练习
写程序,推断在不同的继承方式下,派生类继承 的基类成员的访问限制属性 不同继承方式下的访问限制属性如下:
继承方式 基类中的访问属性 Private 公有继承 Protected Public private 保护继承 protected Public private 私有继承 protected Public 派生类中的访问属性 为基类私有 保护类型 公有类型 为基类私有 保护类型 保护类型 为基类私有 私有类型 私有类型
派生类与基类间的转换
如果函数的参数是基类对象或者基类对象的引用,则相应的实参可以是 派生类对象。
void Func1(CBase base) { //... } void Func2(CBase &base) { //... } CDerived derived; Func1(derived); Func2(derived)
//正常 //引起二义性 //通过成员名限定消除二义性,访问从CBaseA中继承来的m_i //通过成员名限定消除二义性,访问从CBaseB中继承来的m_i //引起二义性 //通过成员名限定消除二义性,访问从CBaseA中继承来的成员函数Func() //通过成员名限定消除二义性,访问从CBaseB中继承来的成员函数Func()
如果用户定义了基类的拷贝构造函数,而没有定义派生类的拷贝 构造函数,那么在用一个派生类对象初始化新的派生类对象时, 两对象间的派生类部分执行缺省的行为,而两对象间的基类部分 执行用户定义的基类拷贝构造函数。 如果用户重载了基类的对象赋值运算符=,而没有定义派生类的 对象赋值运算符,那么在用一个派生类对象给新的派生类对象赋 值时,两对象间的派生类部分执行缺省的赋值行为,而两对象间 的基类部分执行用户定义的重载赋值函数。 如果用户定义了派生类的拷贝构造函数或者重载了派生类的对象 赋值运算符=,则在用已有派生类对象初始化新的派生类对象时, 或者在派生类对象间赋值时,将会执行用户定义的派生类的拷贝 构造函数或者重载赋值函数,而不会再自动调用基类的拷贝构造 函数和基类的重载对象赋值运算符,这时,通常需要用户在派生 类的拷贝构造函数或者派生类的赋值函数中显式调用基类的相应 函数。 见备注中的代码
则内存布局形式为
int m_i int m_ia int m_i int m_ib int Βιβλιοθήκη Baidu_id
CBase
CBaseA
CBase CBaseB
CDerived
虚基类
而如果类CBaseA和CBaseB的声明形式为: class CBaseA:virtual public CBase { public: int m_ia; }; class CBaseB:virtual public CBase { public: int m_ib; }; 则类的体系图为:
//系统会自动将pa指向对象d的CBaseA部分 //系统会自动将pb指向对象d的CBaseB部分
虚基类
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基 类,则在生成派生类对象时,系统会为派生类对象生成共同基类数据 成员的多份拷贝。如果希望派生类对象中只包含一份共同基类的数据 成员,则可以在声明派生类时,通过virtual继承方式,使派生对象只 保留共同基类的一份数据成员。例:(见备注中的代码)
继承和派生一般性概念
在一个已经存在的类的基础上建立一个新的类, 在一个已经存在的类的基础上建立一个新的类, 新类 从已有的类那里获取某些已有的特征, 从已有的类那里获取某些已有的特征,这种现象称为 类的继承。从另一个角度说, 类的继承。从另一个角度说,从已有的类产生一个新 的类,称为类的派生。 的类,称为类的派生。见备注中的代码 如备注中的代码,CBase类派生了一个新的类 备注中的代码,CBase类派生了一个新的类 CDerived,或者说类CDerived从类CBase继承而来, CDerived,或者说类CDerived从类CBase继承而来, 这样,类CDerived不但具有新增的成员m_iDerived和 这样,类CDerived不但具有新增的成员m_iDerived和 DerivedFunc(),还有从类CBase继承而来的成员 DerivedFunc(),还有从类CBase继承而来的成员 m_iBase和BaseFunc(),从而类CDerived实际具有4 m_iBase和BaseFunc(),从而类CDerived实际具有4个 成员。 派生类的一般声明形式为: class 派生类名:[继承方式] 基类名 派生类名:[继承方式] { 派生类新增的成员 };
继承和派生
学习目标
1、掌握继承和派生一般性概念 2、掌握类的继承方式 3、掌握派生类对基类成员的覆盖 4、掌握派生类对象的初始化与清除 5、掌握对象间的转换与赋值 6、掌握派生类与基类间的转换 7、掌握派生类与派生类间的赋值(拷贝) 、掌握派生类与派生类间的赋值(拷贝) 8、掌握保护构造函数与私有构造函数 9、掌握多重继承与虚基类 10、掌握多重继承 10、掌握多重继承 11、掌握虚基类 11、掌握虚基类
派生类对象的初始化与清除 执行派生类构造函数的顺序为: 1、 调用基类的构造函数,初始化基类 的数据成员; 2、 初始化派生类的数据成员; 3、 执行派生类的构造函数本身。 派生类的析构函数相对简单 ,与无继承关 系的普通类的析构函数形式相同。执行 析构函数的顺序为: 1、 调用派生类的析构函数; 2、 调用基类的析构函数。