一、继承的概念及定义
1、概念
继承允许在保持原有类特性的基础上进行扩展。以前的函数是一种代码的复用,而继承是类设计层次的复用。
2、定义
①定义格式
以 person 与 student 类为例,person是student的基类(父类),而student则是派生类(子类)具体的定义如下:
②继承关系和访问限定符
访问限定符是在类中对成员的修饰,和继承关系限定符一起决定了最终的继承方式:
③继承基类成员访问方式的变化
总结:
a、基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。
b、基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在 派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
c、实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
d、使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。(这也是struct和class最大的区别)
e、在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
二、基类和派生类对象赋值转换
①派生类对象可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 。寓意把派生类中父类那部分切来赋值过去。
②基类对象不能赋值给派生类对象。
③基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类 的指针是指向派生类对象时才是安全的。
切片的过程:子类的结构体中,会有和父类一样的成员,也会有自己的成员。切片时就会把父类的这些成员切出去。
三、继承中的作用域
①在继承体系中基类和派生类都有独立的作用域。
②子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
③需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
④注意在实际中在继承体系里面最好不要定义同名的成员。
四、派生类的默认成员函数
1、派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2、派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3、派生类的operator=必须要调用基类的operator=完成基类的复制。
4、派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能 保证派生类对象先清理派生类成员再清理基类成员的顺序。
5、派生类对象初始化先调用基类构造再调派生类构造。
6、派生类对象析构清理先调用派生类析构再调基类的析构。
7、后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加 virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
五、继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
这里出错信息 是student的保护和私有成员不可访问
六、继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子 类,都只有一个static成员实例。
通过这个实例可以统计基类以及派生类实例化的对象个数。
七、菱形继承和菱形虚拟继承
1、多继承
2、菱形继承
有了多继承,就很容易衍生出菱形继承;菱形继承是多继承的一种特殊情况。
3、菱形继承的问题
菱形继承具有数据冗余和二义性的问题。从以下的结构模型可以看出来:
4、如何解决菱形继承的问题——虚拟继承
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和 Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
虚拟继承的形式:在腰上的两个继承前加上virtual
5、虚拟继承的原理
普通的菱形继承:有数据冗余
下面是菱形虚拟继承:
D对象中将A放到的了对象组成的最下 面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
结构模型如下:
八、继承与组合
public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。组合的意思就是,将A作为B的一个成员。
优先使用对象组合,而不是类继承。