没有 virtual
继承 withoutvirtual时,可以想象内存结构是这样的:
class B : A {};

的变量A是类的变量“内部”(就在上面)B。
与 virtual
继承时virtual,B只有一个“指针”指向A:
class B : virtual A {};

您的示例
这意味着,您的示例如下所示:
class B : A
class C : B, virtual A

只有一个实例A
如果你只想要一个实例A,你必须使用virtual两次:
class B : virtual A
class C : B, virtual A

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表示A-subobject in B,即 in C,以区分A-subobject (C.B.A和C.A)。
每个子类都需要一个 v 指针。但是*(C.B.A)和都指向同一个位置(即类的开头也是和的开头*(C.B),因此它们可以共享一个 v-pointer。但是指向子类的指针将指向不同的位置,因此另一个 vpointer 是那里需要。另请注意,在您的示例中甚至无法访问子类(gcc:“警告:由于歧义,在 'C' 中无法访问直接基础'A'”)。*CCC.BC.B.A*(C.A)C.B.A
为了更清楚一点:
如果您只有以下结构
class B : A {};
class C : B {};
只需要一个 vpointer,因为指向任何子类的所有指针都C将指向同一位置。vpointer 只需要指向其中一个的 vtable A,B或者C告诉哪个是对象的运行时类型。