钻石问题和对公共基础成员的模棱两可的调用可以通过下面的等效图形来最好地描述,它也提供了对内存模型的洞察
示例 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 时,Compiler 为 B 和 C 创建一个 Vtable,并在其每个 vtable 中创建一个 vptr 来引用基类 A 的偏移量。这最终解决了 Diamond 问题因为通过 B 或 C 只能看到一份副本
A
/ \
/ \
B C
\ /
\ /
D