在涉及虚函数的虚拟继承的情况下,有人可以解释一下类的大小。
class A{
char k[ 3 ];
public:
virtual void a(){};
};
class B : public A{
char j[ 3 ];
public:
virtual void b(){};
};
class C : public virtual A{
char i[ 3 ];
public:
virtual void c(){};
};
class D : public B, public C{
char h[ 3 ];
public:
virtual void d(){};
};
类的大小输出为:
sizeof(A): 8
sizeof(B): 12
sizeof(C): 16
sizeof(D): 32
我正在使用的编译器是
gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3)
答案 0 :(得分:7)
sizeof(A):8
数组中的3个字节,1个字节的填充,4个字节的vptr(指向vtable的指针)
sizeof(B):12
子对象:8,3个字节用于额外阵列,1个字节填充
sizeof(C):16
这对你来说可能是一个令人惊讶的... 一个子对象:8,3个字节用于额外数组,1个字节填充, 4个字节指向A
每当您具有虚拟继承时,虚拟基础子对象相对于完整类型的开头的位置是未知的,因此将额外的指针添加到原始对象以跟踪虚拟基础的位置。考虑这个例子:
struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};
当完整类型为A
时,B
相对于B
对象开头的位置可能与A
子对象的位置不同B
当它是最终对象D
的一部分时。如果这不明显,请假设相对位置相同,并检查A
最终对象中C
相对于C
的相对位置或{{1} <{1}}中的子对象也可以维护。
至于最后一个例子,我不太喜欢分析它......但你可以阅读Itanium C++ ABI来了解C ++对象模型的具体实现。所有其他实现没有太大差别。
最后一个例子:
sizeof(D):32
D包含一个B子对象(12)和一个C子对象(16),以及一个大小为3的额外数组和一个额外的填充数据1.
在这种特殊情况下,可能出现的问题是,如果C
实际上从D
继承,那么为什么会有两个A
子对象,答案是虚拟基数意味着object愿意与层次结构中也愿意共享它的任何其他类型共享此基础。但在这种情况下,C
不愿意共享它的A
子对象,因此B
需要它自己。
您应该能够通过向不同级别的构造函数添加日志来跟踪此情况。在A
的情况下,它在编译器中取值并从每个扩展类传递不同的值。
答案 1 :(得分:1)
sizeof(C)
超过sizeof(B)
,因为C类型的对象(因为它实际上是从A继承)将包含一个指针(除了类型B的对象也将包含的vptr)指向对于自己继承自A. Scott Meyers本身的部分,在第24项中详细解释了这一点(大约10页):'了解他的书中的虚函数,多继承,虚基类和RTTI的成本'{{ 3}}
答案 2 :(得分:0)
要知道数据结构的实际大小,您可以说编译器不使用#pragma pack(1)将其与内存对齐。要保存当前的打包设置并在以后恢复它们,您还可以使用#pragma pack(push)和#pragma pack(pop)。
答案 3 :(得分:-1)
这是我对所有字节使用位置的最佳猜测:
Avptr Bvptr CVptr DVptr k j i h k' j' i' TOTAL
============= ========================================= =====
sizeof(A): 8 4 4 8
sizeof(B): 12 4 4 4 12
sizeof(C): 16 4 4 4 4 16
sizeof(D): 32 4 4 4 4 4 4 4 4 32
其中: