我认为sizeof(Base)
应该是12.为什么它是16?
没有虚函数,我得到4和8。
class Base{
public:
int i;
virtual void Print(){cout<<"Base Print";}
};
class Derived:public Base{
public:
int n;
virtual void Print(){cout<<"Derived Print";}
};
int main(){
Derived d;
cout<<sizeof(Base)<<","<<sizeof(d);
return 0;
}
预期结果:12,16
实际结果:16,16
为什么sizeof(Base)与sizeof(Derived)没有区别
由于编译器引入了对齐。
这是依赖于体系结构的,但为了简单起见,我假设我们指的是64位体系结构。
Base
类型的对齐方式是8
字节:
alignOfBase(): # @alignOfBase()
mov eax, 8
ret
Base
的布局由变量成员(int
)和虚拟表(vtptr
)组成。
如果我们假设一个“共同”架构,其中:
int
大小为4个字节。vtptr
是一个指针。在64位架构上是8字节大小。如你所料,我们应该有一个4 + 8 = 12
的总和。
但是,我们需要记住Base
的对齐方式是8 bytes
。因此,连续的Base
类型应存储在8的位置倍数。
为了保证这一点,编译器为Base
引入了填充。这就是Base
大小为16字节的原因。
例如,如果我们考虑2个连续的Base
(base0
和base1
)而没有填充:
0: vtptr (base 0) + 8
8: int (base 0) + 4
12: vtptr (base 1) + 8 <--- Wrong! The address 12 is not multiple of 8.
20: int (base 1) + 4
填充:
0: vtptr (base 0) + 8
8: int (base 0) + 4+4 (4 padding)
16: vtptr (base 1) +8 <--- Fine! The adress 16 is multiple of 8.
24: int (base 1) +4+4 (4 padding)
同样的故事是针对Derived
类型的。
Derived
的布局应该是:vtptr + int + int
,即8 + 4 + 4 = 16
。
Derived
的排列也是8
:
alignOfDerived(): # @alignOfDerived()
mov eax, 8
ret
实际上,在这种情况下,不需要引入填充以保持Derived
与存储器对齐。布局大小将与实际大小相同。
0: vtptr (Derived 0)
8: int (Derived 0)
12: int (Derived 0)
16: vtptr (Derived 1) <---- Fine. 16 is multiple of 8.
24: int (Derived 1)
28: int (Derived 1)
这是因为编译器决定对齐您的类。
如果您希望(或需要)结构或类具有“真实”大小,您可以像这样使用#pragma pack(1)
:
#pragma pack(push, 1) // Set packing to 1 byte and push old packing value to stack
class Base{
public:
int i;
virtual void Print(){cout<<"Base Print";}
};
class Derived:public Base{
public:
int n;
virtual void Print(){cout<<"Derived Print";}
};
int main(){
Derived d;
cout<<sizeof(Base)<<","<<sizeof(d);
return 0;
}
#pragma pack(pop) // restore old packing value