3
struct A {
int i;
virtual void f() { cout << i; }
A() { i = 1; }
A(int _i) : i(_i) {}
};

struct B : A {
B() : A(2) { f(); }
void f() { cout << i+10; }
};

struct C : B, virtual A {
C() : A() {}
};

您能否解释一下为什么在 C 结构中会有两个A::i以及为什么会有两个指向C结构的“虚拟表方法”的vptr指针,尽管A只有虚拟方法?我知道当从一个公共基础进行虚拟继承时,公共基础的一个实例而不是两个,请指教!

4

2 回答 2

14

没有 virtual
继承 withoutvirtual时,可以想象内存结构是这样的:

class B : A {};

没有虚拟的继承

的变量A是类的变量“内部”(就在上面)B

与 virtual
继承时virtualB只有一个“指针”指向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

只有一个 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.AC.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 AB或者C告诉哪个是对象的运行时类型。

于 2013-04-09T15:46:56.680 回答
1

B没有继承虚拟。

所以你有从 B 继承的 A 结构和另一个继承的虚拟结构。

于 2013-04-09T15:25:23.937 回答