本文同步自(如浏览不正常请点击跳转):https://zohead.com/archives/cplusplus-virtual-base-class/
在C++的某些应用环境中,如果有多个派生类同时继承同一个基类,但只要想基类成员的一份拷贝,这样可以把对基类的继承声明为虚拟(virtual)的,以达到需要的目的,用 virtual 限定的继承即为虚继承,对应的基类即为虚基类,没有则为非虚基类。在派生类产生的对象中,同名的虚基类只产生一个虚基类对象,非虚基类则有各自的对象,需要注意虚基类和抽象类的区分。
另外特别需要注意的是一个基类即可以被用作虚基类,也可以被用作非虚基类,这纯粹看是否是虚继承(是否有 virtual 限定)。虚基类子对象是由最远派生类(也就是建立对象时所指定的类)的构造函数调用虚基类的构造函数进行初始化。如果一个派生类继承了多个基类,其中既有虚基类,也有非虚基类,则虚基类构造函数优先于非虚基类的构造函数,而不管实际书写顺序,而且如果该派生类的构造函数初始化列表中同时有对虚基类和非虚基类构造函数的调用,同样也是虚基类优先。
有关虚基类的其它说明请参考其它书籍咯,下面是测试的代码:
#include <iostream> using namespace std; class Base { public: explicit Base(int val) : value(val) { cout << "init Base " << this << ", value:" << value << endl; } ~Base() { cout << "destory Base " << this << ", value:" << value << endl; } private: int value; }; class Base2 : public virtual Base { public: Base2(int val) : Base(val) { cout << "init Base2" << endl; } }; class Base3 : public virtual Base { public: Base3(int val) : Base(val) { cout << "init Base3" << endl; } }; class Base4 : public Base2, public virtual Base3 { public: Base4(int val) : Base2(val), Base3(val), Base(val) { } }; int main(int argc, char ** argv) { Base4 base_4(22); return 0; }比较简单,Base2 和 Base3 分别派生自 Base,而且都是虚继承,Base4 同时派生自 Base2 和 Base3,但 Base3 前有 virtual,表示 Base4 对 Base3 是虚继承,Base4 对 Base2 是非虚继承。另外代码中比较特殊的一点是 Base 基类中去掉了默认构造函数,改为一个 int 参数的构造函数(第 8 行),因此 Base2 和 Base3 的构造函数的初始化列表中都需要增加对基类 Base 构造函数的调用,同样 Base4 需要调用 Base、Base2 和 Base3。
主程序中只是生成一个 Base4 的对象,上述代码的运行输出为:
init Base 0x22ff34, value:22
init Base3
init Base2
destory Base 0x22ff34, value:22
由上面的输出可以验证虚基类的概念:
由于 Base2 和 Base3 都是对 Base 的虚继承,虽然 Base4 中分别调用了 Base2 和 Base3 的构造函数,但虚继承就表示基类 Base 只有一份拷贝,Base 只需要构造一次。另外由于 Base4 对 Base2 是非虚继承,对 Base3 是虚继承,尽管 Base2 看起来在前面调用,但由于 Base3 对 Base4 是虚基类,它的构造函数就需要先于 Base2 对调用,因此 ”init Base3“ 在 ”init Base2“ 前面。
接着对代码稍作改动,将 Base2 和 Base3 都改为非虚继承,Base4 中去掉对 Base 基类构造函数的调用:
第22行:class Base2 : public Base
第31行:class Base3 : public Base
第43行: Base4(int val) : Base2(val), Base3(val)
编译运行输出,结果为:
init Base 0x22ff38, value:22
init Base3
init Base 0x22ff34, value:22
init Base2
destory Base 0x22ff34, value:22
destory Base 0x22ff38, value:22
可以看到由于 Base2 和 Base3 都不是非虚继承,Base 就被构造了两次。
在上面的基础上再做个小改动,把 Base4 对 Base3 的派生也改为非虚继承:
第40行:class Base4 : public Base2, public Base3
再次运行输出,结果即为:
init Base 0x22ff30, value:22
init Base2
init Base 0x22ff34, value:22
init Base3
destory Base 0x22ff34, value:22
destory Base 0x22ff30, value:22
由于 Base4 对 Base2 和 Base3 都不是虚继承了,对 Base2 和 Base3 的构造函数调用就按实际顺序了。
这样虚基类的概念就算是比较清楚咯。 ^_^