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

What happens to a virtual base class on being derived in multilevel inheritance?

本文关键字:基类 什么 虚拟 多级 继承 派生      更新时间:2023-10-16

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

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;
}

为什么会有这种差异?为什么是第二种情况的两倍?

这取决于实现

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

严格地说,你应该依赖大小来确定任何特定的大小,这就是为什么标准提供sizeof来获得实际大小,而不是猜测估计它的原因。

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

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

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成员:来自C定义的k和来自A定义的i。类似地,类型为D的对象具有两个int成员,即li。到目前为止,一切都很好。类E的棘手部分是:它也有一个int成员i,因为A的两个实例都是虚拟基。因此,CD都不能像上面的B0那样编写,因为E最终会有两个副本的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的大小中,您看到的是那些额外的指针,它们使得无论CD在派生类中如何组合,都可以拥有i的单个副本。(实际上,这些指针中的每一个都是指向A的指针,因为A当然可以有多个数据成员,但这在这个简单的伪代码中太难表示了)。

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