没有虚拟
当不使用virtual
进行继承时,您可以将内存结构想象成这样:
class B : A {};
![Inheritance without virtual](https://istack.dev59.com/g8bxX.webp)
A
的变量位于(就在上面)类B
的变量之内。
使用虚拟继承
当使用virtual
进行继承时,B
只是拥有一个指向A
的"指针":
class B : virtual A {};
![Inheritance with virtual](https://istack.dev59.com/H2NUy.webp)
你的例子
这意味着,你的例子看起来像这样:
class B : A
class C : B, virtual A
![Your example](https://istack.dev59.com/8huMz.webp)
只有一个实例的 A
如果你只想要一个 A
的实例,你必须使用两次 virtual
:
class B : virtual A
class C : B, virtual A
![Only one instance of A](https://istack.dev59.com/JzTN2.webp)
vptr 的
这是您的代码布局,由 g++ 生成(使用 -fdump-class-hierarchy
生成):
Class C
size=16 align=4
base size=8 base align=4
C (0xb7193440) 0
vptridx=0u vptr=((& C::_ZTV1C) + 12u)
B (0xb719f078) 0
primary-for C (0xb7193440)
A (0xb719a428) 0
primary-for B (0xb719f078)
A (0xb719a460) 8 virtual
vptridx=4u vbaseoffset=-12 vptr=((& C::_ZTV1C) + 28u)
我对vpointers和vtables有点生疏,所以我不确定为什么会有那些v-pointers。
然而,我可以告诉你,你的假设只有一个虚方法是错误的。
因为B
继承自A
,并且A::f()
是虚拟的,派生的B::f()
也自动成为虚拟的,即使你没有明确写下这一点。
编辑:
经过一番挖掘,我想我记得哪些v-pointers是必需的,哪些不是。
但是我不能保证以下内容。
在下面,符号C.B.A
表示在C
中的B
子对象中的A
子对象,以区分两个A
子对象(C.B.A
和C.A
)。
每个子类都需要一个v指针。然而,*(C.B.A)、*(C.B)和*C都指向同一位置(即类C的开头,也是C.B和C.B.A的开头),因此它们可以共享一个v指针。但是,指向子类*(C.A)的指针将指向不同的位置,因此还需要另一个v指针。另外,请注意,子类C.B.A在您的示例中甚至无法访问(gcc:"warning: direct base ‘A’ inaccessible in ‘C’ due to ambiguity")。
为了使其更清晰一些:如果你只有以下结构
class B : A {};
class C : B {};
只需要一个虚指针,因为指向任何C
子类的指针都将指向同一位置。虚指针只需指向A
、B
或C
中的任意一个的虚表,即可告知对象的运行时类型。