C++继承和派生
Part Ⅸ 继承和派生
继承的概念
继承是在一个已存在的类的基础上,建立一个新的类
已存在的类称为“基类(base class)”或“父类(father class)”
C++中,顶层的类就是最终基类。没有最终基类Object
新建立的类称为“派生类(derived class)”或“子类(child class)”
声明派生类
class ClassName: <Access> BaseClassName
派生类可以:
- 继承基类的成员数据或成员函数
- 增加新的成员数据和新的成员函数
- 重新定义已有的成员函数,覆盖基类的同名成员,父类定义功能,子类提供实现
单继承与多继承
- 一个派生类只从一个基类派生,称为单继承
- 一个派生类有两个或多个基类,称为多重继承
访问权限
class subClass : public/protected/private BaseClass
- 公有派生,基类中所有成员在派生类中保持访问权限
- 保护派生时,基类中公有成员在派生类中均变为保护的
- 私有派生时,基类中所有成员在派生类中均变为私有的
派生类的构造函数
要么调用基类的构造函数,要么调用自己的另一个构造函数, 二选一
派生类的构造函数调用不能出现递归
派生类如果有多个构造函数,必然有至少一个调用基类的构造函数
必须在初始化列表中调用
构造的顺序
- 执行参数初始化列表
- 调用基类的构造函数,初始化基类成员
- 按照成员变量定义顺序,依次初始化
- 调用自身构造函数的实现部分
析构顺序相反
如果派生类构造函数中没有主动调用构造函数,则自动调用基类无参数构造函数
基类如果没有无参构造函数,则派生类需要明确的指定调用哪一个,否则编译错误
派生类新增加的成员初始化可以通过参数初始化表,也可以在构造函数内部实现
继承的说明
基类是单独编译的,如果不修改基类,在程序重新编译时只需对派生类新增的功能进行编译
如果修改了基类实现,只要基类的公用接口不变,派生类不必重新编译
多重继承
派生类构造函数名(总参数表列) : 基类1构造函数(参数表列) , 基类2构造函数(参数表 列) , 基类3构造函数(参数表列){派生类中新增数成员据成员初始化语句}
派生类构造函数的执行顺序:先调用基类的构造函数,再执行自身构造函数
当派生类中新增加的数据或函数与基类中成员同名时,缺省优先调用派生类中的成员
多个基类数据或函数同名时,利 用类作用域符号::来指明数据或函数的来源
同一个公共的基类在派生类中产生多个拷贝,多占用空间,容易混淆
虚基类
在多重派生的过程中,若使公共基类在派生类中只有一个拷贝,可将公共基类说明为虚基类
在派生类的定义中,在基类的类名前加上关键字virtual,将基类说明为虚基类
class B: virtual public A {}
由虚基类派生出的对象初始化时,直接调用虚基类的构造函数
能用单一继承解决的问题就不要使用多重继承,在比较简单、不易出现二义性 的情况,或实在必要时才考虑多重继承
多态
多态:向不同的对象发送同一个消息,不同的对象会产生不同的行为
- 静态多态,编译时的多态 (函数重载,运算符重载)
- 动态多态,运行时的多态(虚函数)
基类的指针可以指向子类(派生类)对象
C++中,若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数,只有用virtual修饰的函数才是可以被覆盖的
virtual float area(void){return 0.0;}
为什么?多态影响执行效率,要主动声明
派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数,以及是否缺省,必须一一对应,函数的返回类型也相同
Virtual析构函数
父类(基类)的析构方法一般为virtual,保证派生类的析构方法得到执行
虚函数
虚函数是用关键字virtual修饰的某基类中非private成员函数。它可以在派生类中重 新定义,以形成不同版本
使用虚函数多态性,必须使用基类类型的指针变量,使该指针指向其派生类对象
通过对象名访问虚函数时,只能静态绑定,不能多态。多态必须是p->m(),不能是p.m()
虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数
可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数
多态即动态绑定是有代价的,虚函数与一般的成员函数相比较,调用时的执行速度要慢一些
一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性
纯虚函数
在基类中不对虚函数给出实现,在派生类中进行实现。这时基类中的虚函数只是一个入口,具体的操作由派生类中的对象实现。这种虚函数称为纯虚函数(对应Java 抽象方法)
至少包含一个纯虚函数的类,称为抽象类(abstract classes)。没有特殊关键字声明抽象类。抽象类只能作为基类,不能用来创建对象
在定义纯虚函数时,不能定义虚函数的实现部分。把函数名赋0,本质上是将函数代 码段为0,即没有代码段实现
class A{
public:
virtual void print()=0
}

浙公网安备 33010602011771号