1

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

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

4 回答 4

2

取决于实现

然而,几乎所有的编译器都会使用相同的机制,只要你有一个关键字,编译器就需要通过andvirtual做一些额外的记录。这种额外的簿记增加了班级规模。vptrvtables

严格来说,您应该依靠尺寸来确定任何具体的尺寸,这就是标准提供sizeof获取实际尺寸而不是猜测它的原因。

于 2012-12-08T10:59:07.527 回答
2

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

请参阅虚拟表和虚拟指针中的问题 4,用于多重虚拟继承和类型转换以及答案。

于 2012-12-08T10:59:17.223 回答
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来自​​ 的定义Ci来自 的定义A。类似地,一个类型的对象D有两个int成员,li。到目前为止,一切都很好。棘手的部分来自 class E:它也有一个 intmember i,因为 的两个实例A都是虚拟基础。所以既不C是也D不能像B0上面那样写,因为那样E最终会得到两个.i

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

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都可以拥有一个副本。(实际上,这些指针中的每一个都是指向 的指针,因为肯定可以有多个数据成员,但这在这个简单的伪代码中很难表示)。iCDAA

于 2012-12-08T13:33:51.287 回答
0

这取决于您的编译器的实现。不同的编译器有不同的结果。但一个是肯定的,结果肯定不止三个。

于 2012-12-08T12:17:25.103 回答