没有 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'”)。*C
C
C.B
C.B.A
*(C.A)
C.B.A
为了更清楚一点:
如果您只有以下结构
class B : A {};
class C : B {};
只需要一个 vpointer,因为指向任何子类的所有指针都C
将指向同一位置。vpointer 只需要指向其中一个的 vtable A
,B
或者C
告诉哪个是对象的运行时类型。