在C++中菱形继承会有数据冗余的问题发生,我们可以使用虚继承来解决,那虚继承的原理是什么,为什么它可以解决这个问题。
菱形继承的数据冗余问题
class A {
public:int data;
};class B : public A {};class C : public A {};class D : public B, public C {};
在上述代码中,类 D 的对象在内存中会包含两份来自类 A 的成员变量 data(一份通过 B 继承而来,另一份通过 C 继承而来),这就导致了资源冗余的问题,并且在访问 data 成员时可能会产生二义性(因为不清楚具体要访问哪一份 data)
虚继承
class A {
public:int data;
};class B : virtual public A {};class C : virtual public A {};class D : public B, public C {};
虚继承的实现机制主要通过在虚继承体系下创建一个虚基类表(virtual base class table,简称 vbtable)和调整对象的内存布局来解决资源冗余和二义性问题,具体如下:
在内存角度:
在非虚继承的菱形继承中,派生类 D 的对象内存布局会依次包含 B 类部分(其中包含一份 A 的成员)和 C 类部分(其中又包含一份 A 的成员)。
而在虚继承情况下,派生类 D 的对象内存布局会先有一个指向虚基类表(vbtable)的指针(如果有多个虚基类,可能只有一个这样的指针,具体实现依赖于编译器),然后是 B 类和 C 类各自特有的成员部分,而对于公共的虚基类 A 的成员,只会存在一份,通常放在对象内存布局的末尾(同样,具体位置和布局方式由编译器决定,但关键是只有一份)。
虚基类表的作用:
虚基类表用于记录每个虚继承的派生类与虚基类之间的偏移量等信息。
对于类 D,它通过虚继承同时继承了 B 和 C,而 B 和 C 又虚继承了 A。当 D 的对象需要访问虚基类 A 的成员时,会先通过对象中的虚基类表指针找到虚基类表,然后根据表中的偏移量信息,准确地定位到唯一一份虚基类 A 的成员所在位置,从而避免了访问到重复的成员,解决了资源冗余和二义性的问题。