Derived
需要一些方法来知道Base
子对象在哪里。使用虚拟继承,基类的相对位置相对于派生类的位置不是固定的:它可能位于完整对象中的任何位置。
考虑一个更典型的涉及钻石继承的例子。
struct A
{
int a;
};
struct B1 : virtual A
{
int b1;
};
struct B2 : virtual A
{
int b2;
};
struct C : B1, B2
{
int c;
};
在这里,B1
和B2
都从 虚拟派生A
,所以在 中C
,只有一个A
子对象。两者都B1
需要B2
知道如何找到该A
子对象(以便他们可以访问成员变量,或者如果我们要定义它们 a
的其他成员)。A
在这种情况下,这就是 vtable 的用途:两者B1
都有B2
一个包含A
子对象偏移量的 vtable。
为了演示编译器如何实现上述菱形继承示例,请考虑以下由 Visual C++ 11 开发人员预览生成的类布局和虚拟表。
class A size(4):
+---
0 | a
+---
class B1 size(12):
+---
0 | {vbptr}
4 | b1
+---
+--- (virtual base A)
8 | a
+---
class B2 size(12):
+---
0 | {vbptr}
4 | b2
+---
+--- (virtual base A)
8 | a
+---
class C size(24):
+---
| +--- (base class B1)
0 | | {vbptr}
4 | | b1
| +---
| +--- (base class B2)
8 | | {vbptr}
12 | | b2
| +---
16 | c
+---
+--- (virtual base A)
20 | a
+---
和以下虚表:
B1::$vbtable@:
0 | 0
1 | 8 (B1d(B1+0)A)
B2::$vbtable@:
0 | 0
1 | 8 (B2d(B2+0)A)
C::$vbtable@B1@:
0 | 0
1 | 20 (Cd(B1+0)A)
C::$vbtable@B2@:
0 | 0
1 | 12 (Cd(B2+0)A)
请注意,偏移量是相对于 vtable 的地址的,请注意,对于为B1
和的B2
子对象生成的两个 vtable C
,偏移量是不同的。
(另请注意,这完全是一个实现细节——其他编译器可能会以不同的方式实现虚函数和基。这个例子展示了它们的一种实现方式,并且它们非常普遍地以这种方式实现。)