第四章 继承与派生
4.1 继承与派生的概念
类的继承:是新的类从已有类那里得到已有的特性
类的派生:从已有类产生新类的过程
派生新类的过程包括3个步骤:吸收基类成员、改造基类成员和添加新成员。
已存在的类称为基类 、“父类”或“一般类”。 新建立的类称为“派生类”**、“子类”或“特殊类”。
派生类是基类的具体化,而基类是派生类的抽象。
派生类继承了基类的所有数据成员和成员函数(不包括基类的构造函数和析构函数)
4.2 派生类的声明
class派生类名: [继承方式]基类名
{
派生类新增加的成员
};
class Cylinder: public Circle//以public方式声明派生类Cylinder-圆柱体类
{
......
};
4.3 派生类的构成
继承过来的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性
构造一个派生类一般经历3个步骤:从基类接收成员、调整从基类接收的成员、增加新成员
在声明派生类时,一般还应当定义派生类的构造函数和析构函数
4.4 派生类中基类成员的访问属性
公用继承
私有继承
保护继承
保护成员与私有成员不同: 保护成员可以被派生类的成员函数访问。
成员同名问题
4.5 派生类的构造函数和析构函数
派生类的构造函数
派生类构造函数的具体执行过程:先调用基类的构造函数,对派生类对象从基类继承过来的数据成员进行初始化;再执行派生类构造函数的函数体,对派生类对象新增数据成员进行初始化。
派生类的析构函数
在定义派生类析构函数时,不需要显示书写对基类析构函数的调用。系统在执行派生类的析构函数时,会自动调用基类的析构函数。
4.6 多重继承
<总参数表>必须包含完成所有基类数据成员初始化所需的参数。
构造函数调用顺序为:先调用所有基类的构造函数,再执行派生类构造函数的函数体。所有基类构造函数的调用顺序将按照它们在继承方式中的声明次序调用,而不是按派生类构造函数参数初始化列表中的书写次序调用。
多重继承引起的二义性问题(能用单一继承解决的问题就不要使用多重继承)
二义性问题:由于多重继承而引起的对派生类的某个成员访问出现不唯一的情况
(1)两个基类有同名成员。
(2)两个基类和派生类三者都有同名成员
obj.Show(); //正确,访问派生类的Show成员
基类的同名成员在派生类中被屏蔽,成为“不可见"的,即基类成员的名字被隐藏。
(3)两个基类是从同一个基类派生的
//如在职研究生类GradOnWork,它的两个基类Graduate和Employee从同一个基类Person 派生。
obj.Graduate::Show() ;
obj.Employee::Show() ;
obj.Student::Show() ;
//obj.Person::Show(); 错误
虚基类
作用:在继承间接共同基类时只保留其一份成员
class 派生类名:virtual 继承方式 基类名
class Student:virtual public Person
为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中都把基类声明为虚基类。
虚基类的初始化:
如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类(包括直接派生和间接派生的派生类)中,都要通过构造函数的参数初始化表对虚基类进行初始化。
- 不用virtual
- 用虚基类
4.7 基类与派生类对象的关系
基类对象与公用派生类对象之间有赋值兼容关系。
(1)公用派生类对象可以向基类对象赋值。
因为基类对象中不包含派生类的新增成员,无法对派生类的新增成员赋值。同理,同一基类的不同派生类的对象之间也不能进行赋值。
(2)公用派生类对象可以代替基类对象向基类对象的引用进行赋值或初始化。
通过personref只能访问student从基类继承过来的成员,而不能访问student新增的成员
(3)如果函数的参数是基类对象或基类对象的引用,相应的实参可以使用公用派生类对象。
如果函数的参数是指向基类对象的指针,相应的实参可以使用公用派生类对象的地址。
4.8 聚合与组合
聚合关系中成员对象可以脱离整体对象独立存在,而组合关系中的部分和整体具有统一的生命周期。一旦整体对象不存在,,部分对象也将不存在。组合关系也被称为是一种强聚合关系。
在派生类构造函数的总参数列表中,给出了初始化基类数据成员、新增子对象数据成员及新增一般数据成员所需的全部参数。在参数表后,列出基类构造函数名、子对象名及各自的实参表,各项之间用逗号分隔。
包含子对象派生类构造函数的执行顺序:
(1) 最先调用基类的构造函数,对基类数据成员初始化。(按照在继承方式中的声明次序调用)
(2) 再调用子对象的构造函数,对子对象数据成员初始化。
(3) 最后执行派生类构造函数的函数体,对派生类新增一般数据成员初始化。
包含子对象派生类析构函数的执行顺序与其构造函数的执行顺序相反
继承表达的是类与类之间的一种纵向层次关系(is-a) (类与类之间的一般与特殊的关系)
聚合表达的是类与类之间的一种松散的整体和局部的关系(has-a)
组合表达的是一种紧密的整体和局部的关系(contain-a)