菱形继承
我们先来回忆一下C++中的菱形继承
菱形继承的概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承两个派生类
- 这种继承被称为菱形继承,或钻石继承
菱形继承的问题:
- 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性
- 草泥马继承自动物的数据继承了两份,其实我们只需要一份就可以了
解决方法:
- 当菱形继承,两个父类拥有相同的数据,需要加以作用域区分
- 利用虚继承,解决菱形继承的问题
虚继承的对象模型
普通继承是子类拷贝并修改父类的虚函数表;虚继承是子类和父类的虚函数表分开保存,将指向父类的虚函数表的指针也加入到子类的对象模型中。
假定单一虚继承场景如下
例如:
此时派生类Derived的对象模型为:
可以看到:
- 派生类的虚函数表和基类的虚函数表是分开的,派生类重写的虚函数将覆盖基类虚函数表中的同名函数。
- 派生类中新增了一个虚基类指针,它指向一个表,表中保存对象模型中各个虚函数表指针的偏移量,第一项为派生类虚函数表指针的偏移量,第二项为虚继承中第一个基类的虚函数表指针的偏移量,以此类推。
- 派生类的信息与基类的信息用0x00000000隔开。
使用虚继承的菱形继承:
结合多重继承与单一虚继承,推导派生类Derived的对象模型为:
多重继承的布局基本不变,虚基类的信息被追加到内存布局最后,并用0x00000000隔开。此时再访问B::b不会出现二义性。
推导时,从子类往祖父类逐步推进,子类与父类适用多重继承,父类与祖父类适用单一虚继承,每一步只决定派生类的数据成员的位置,例如B::b的布局应该由单一虚继承决定,如果在多重继承时决定,那推出来也是二义的。