在多级继承中派生的虚拟基类会发生什么?

时间:2012-12-08 10:54:48

标签: c++ inheritance virtual-inheritance multi-level memory-layout

在玩继承时,我碰巧尝试了这个:

class A
{ int i; };

class B : virtual public A
{ int j; };

class C : public B
{ int k; };

int main()
{
    std::cout<<sizeof(C)/sizeof(int);
    return 0;
}

这给了我输出6

虽然以下方法按预期工作,但输出3

class A
{ int i; };

class B : public A  // No virtual here
{ int j; };

class C : public B
{ int k; };

int main()
{
    std::cout<<sizeof(C)/sizeof(int);
    return 0;
}

为什么会出现这种差异?而且,为什么它是第二种情况的两倍?

4 个答案:

答案 0 :(得分:2)

依赖于实现

但是,几乎所有编译器都将使用相同的机制,只要您有virtual关键字,编译器就需要通过vptrvtables进行一些额外的簿记。这笔额外的簿记会增加班级人数。

严格来说,你应该依赖大小来确定具体的内容,这就是为什么标准提供sizeof以获得实际大小而不是猜测它的原因。

答案 1 :(得分:2)

很简单,虚拟继承涉及额外的开销。典型的实现至少需要一个额外的指针。

请参阅Virtual tables and virtual pointers for multiple virtual inheritance and type casting中的问题4和答案。

答案 2 :(得分:1)

class A {
    int i;
};

class B : public A {
    int j;
};

在这个使用虚拟继承的示例中,可以布置B类型的对象,就好像B被定义为:

class B0 {
    int i;
    int j;
};

一旦引入虚拟继承,这不起作用:

class C : public virtual A {
    int k;
};

class D : public virtual A {
    int l;
};

class E : public C, public D {
    int m;
};

C类型的对象有两个int成员:来自​​k定义的C和来自i定义的A 。同样,D类型的对象有两个int成员,li。到现在为止还挺好。棘手的部分来自课程E:它也有一个 int成员i,因为A的两个实例都是虚拟基础。因此CD都不能像上面的B0一样编写,因为E最终将会有{{1>}个i }}

解决方案是添加一个间接层。 CDE类型的对象看起来像这样(伪代码,不要尝试编译它):

class C0 {
    int *cip = &i;
    int k;
    int i;
};

class D0 {
    int *dip = &i;
    int l;
    int i;
};

class E0 {
// C0 subobect:
    int *cip = &i;
    int k;
// D0 subobject:
    int *dip = &i;
    int l;
// E data:
    int *eip = &i;
    int m;
    int i;
};

您在E的大小中看到的是那些额外的指针,无论i和{{1}如何,都可以生成C的单个副本在派生类中组合。 (实际上,这些指针中的每一个都是指向D的指针,因为A当然可以有多个数据成员,但在这个简单的伪代码中难以表示。)

答案 3 :(得分:0)

这取决于编译器的实现。不同的编译器有不同的结果但可以肯定的是,结果必须超过三个。