我认为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
答案 0 :(得分:8)
为什么sizeof(Base)与sizeof(Derived)没有不同
由于编译器引入的 alignment 。
那是与体系结构有关的,但是为了简单起见,我假设我们指的是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)
答案 1 :(得分: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