钻石问题和对公共基础成员的模糊调用可以通过以下等效图示来最好地描述,该图示也给出了对内存模型的深入了解
实施例1
class A
{void foo(){};};
class B :public A
{};
class C :public A
{};
class D :public B,public C
{};
由于继承只是将两个对象的实现依次放置,因此基类 A 的所有方法都会在派生类 B 和 C 中重复,其等效的图形表示为
A A
| |
| |
B C
\ /
\ /
D
实施例2
class A
{};
class B :public virtual A
{};
class C :public A
{};
class D :public B,public C
{};
使用 virtual 关键字,编译器为派生类 B 生成一个 vtable,在 B 的 vtable 中包含一个 vptr(虚拟指针),其中存储基类 A 的偏移量。对于 C,它仍然复制基类 A 的所有方法。所以类D,通过基类 B 通过 vptr 引用基类 A 的成员,其中类 C 将基类 A 的成员作为重复副本引用。因此,钻石问题和模糊性仍然存在。
A A
/ |
/ |
B C
\ /
\ /
D
实施例3
class A
{};
class B :public virtual A
{};
class C :public virtual A
{};
class D :public B,public C
{};
现在,当派生类 B 和 C 都虚拟继承 A 时,编译器会为 B 和 C 创建一个 Vtable,并在每个 Vtable 中创建一个 vptr 以引用基类 A 的偏移量。这最终解决了钻石问题,因为有通过 B 或 C 只能看到一份副本
A
/ \
/ \
B C
\ /
\ /
D