构造函数与析构函数
派生类只能在构造函数初始化列表中为基类或对象成员进行初始化。
当基类没有默认构造函数的情况下,派生类必须定义构造函数,并通过它为基类构造函数提供初始化值。(编译器合成的构造函数也算默认构造函数)
子类构造函数只负责直接基类的初始化。
当存在虚基类时,所有虚基类都由最后的派生类负责初始化。如果没有,则报错。
子类继承基类的构造函数
C++11允许子类继承基类的构造函数,带来的方便是:
当基类构造函数有较多参数,而派生类没有数据成员需要初始化,但它必须提供构造函数,且唯一目的是为基类构造函数提供初始化值。在这种情况下,它只需要继承直接基类的构造函数就可以了。
1 | class Derived:public Base{ |
这个using
不受访问权限的控制,放在public
、private
和protected
区域中没有区别。
但这个using
与另外两种用法(见继承第一讲)有不同之处:这个会使编译器在子类中生成代码,而用using
声明基类成员时,并不生成代码。
构造函数和析构函数的调用次序
基类构造函数$\rightarrow$对象成员构造函数$\rightarrow$子类构造函数(体)
- 多继承时,基类构造函数调用次序为继承方式中声明次序。
- 有多个对象成员时,按它们在子类中的声明次序调用其构造函数。
析构函数与构造函数次序相反。
这个次序要和之后出现虚拟继承后构造函数的次序进行结合,例题可看课本P173。
基类与继承类的使用
根据继承的定义,我们可知任何一个派生类对象的内部都包含一个基类子对象,所以可以通过截取的方法从派生类对象中复制其基类子对象并将之赋值给基类对象。
子类对象对基类对象的赋值和初始化
有两种情况:赋值和初始化。如下:
赋值
1 | Derived d1; |
在把子类对象赋值给基类对象时调用基类的赋值运算符函数。
初始化
1 | Derived d1; |
1 | void func(Base b); |
以上两种情况,在用子类对象初始化基类对象时,会调用基类的拷贝构造函数。
子类指针和基类指针
在这里,在逻辑上引用与指针等价。
基类指针可以指向子类对象
1 | Derived d1; |
子类指针不能指向基类对象
1 | Base b1; |
若想实现类似子类指针指向基类对象的功能,需要用强制类型转换,但也只能在逻辑上正确时才能使用,否则后续使用中绝对会出错的。如下:
1 | Derived d1; |
多继承
定义
多继承即一个类继承了多个类。
1 | class D:public A, B,protected C{ |
成员二义性
多继承情况下,当多个(1个以上)基类拥有同名成员时,会产生成员二义性。
可以通过类域限定符解决该问题,明确指出成员所属的基类。
虚拟继承
虚拟继承引入的原因
解决多继承引出的二义性问题。
如Student
、Employee
继承Person
,StuEmployee
继承了Student
、Employee
,这样StuEmployee
的对象中就有两份不同的Person
的数据成员。
1 | class Student: public Person{...} |
语法
1 | class Student:virtual public Person{...} |
构造函数调用次序
先调用虚基类的构造函数,再调用非虚基类的构造函数。
同一继承层次中有多个虚基类时,就按照它们声明次序进行构造。
若某个虚基类的构造函数已被调用,就不再调用。
若虚基类有非虚基类派生而来,则先调用该虚基类的构造函数,再调用该非虚基类的构造函数。
成员函数优先级
继承层次中越靠下优先级越高。
作者:@臭咸鱼
转载请注明出处:https://chouxianyu.github.io
欢迎讨论和交流!