在玩继承时,我碰巧尝试了这个:
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;
}
为什么会出现这种差异?而且,为什么它是第二种情况的两倍?
答案 0 :(得分:2)
依赖于实现。
但是,几乎所有编译器都将使用相同的机制,只要您有virtual
关键字,编译器就需要通过vptr
和vtables
进行一些额外的簿记。这笔额外的簿记会增加班级人数。
严格来说,你应该依赖大小来确定具体的内容,这就是为什么标准提供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
成员,l
和i
。到现在为止还挺好。棘手的部分来自课程E
:它也有一个 int
成员i
,因为A
的两个实例都是虚拟基础。因此C
和D
都不能像上面的B0
一样编写,因为E
最终将会有{{1>}个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
的大小中看到的是那些额外的指针,无论i
和{{1}如何,都可以生成C
的单个副本在派生类中组合。 (实际上,这些指针中的每一个都是指向D
的指针,因为A
当然可以有多个数据成员,但在这个简单的伪代码中难以表示。)
答案 3 :(得分:0)
这取决于编译器的实现。不同的编译器有不同的结果但可以肯定的是,结果必须超过三个。