我有一个简单的 C++ 类,它有一个整数和一个 vtable:
class Something {
virtual void sampleVirtualMethod();
int someInteger;
};
如果您查看 MSVC 的对象布局(使用 /d1reportSingleClassLayout),您会得到:
class Something size(8):
+---
0 | {vfptr}
4 | someInteger
+---
这是完全有道理的。 4 个字节用于 vtable 指针,4 个字节用于整数。奇怪的是,当我向类中添加一个 double 时:
class Something {
virtual void sampleVirtualMethod();
int someInteger;
**double someDouble;**
};
我得到这个对象布局:
class Something size(24):
+---
0 | {vfptr}
8 | someInteger
| <alignment member> (size=4)
16 | someDouble
+---
为什么 0 偏移量和 someInteger 之间的差异是 8 而不是 4? vtable 是否以某种方式增长到 8 字节?无论我添加双精度值的顺序如何,都会发生这种情况。
这篇博文讨论了同样的问题,并在很久以前编写布局代码 MS C++ 编译器的 Jan Gray 的评论中提供了解释。
换句话来说,只有在其他数据成员布局完毕之后,vfptr 才会插入到类布局中。根据数据成员的对齐要求,这意味着可能会引入不必要的填充。此外,只有当该类没有带有 vfptr 的基类时,才会发生这种情况。
因此,博客文章中介绍了解决方法:
class EmptyBase
{
protected:
virtual ~EmptyBase() {}
};
class Something : public EmptyBase {
virtual void sampleVirtualMethod();
int someInteger;
**double someDouble;**
};
在这种情况下,sizeof(Something)
应为 16。
我怀疑这个答案与此有关。引用 dirkgently 的回答,引用 GCC 手册:
请注意,ISO C 标准要求任何给定结构体或联合体类型的对齐方式至少是相关结构体或联合体所有成员的对齐方式的最低公倍数的完美倍数。
根据该规则,一旦添加了 8 字节双精度数,编译器就必须将所有内容排列为 8 字节倍数。当然,您可以使用 #pragma pack() 覆盖它,但如果您最终在 8 字节边界以外的其他位置上使用 8 字节成员,效率会较低。
我无法直接回答你的问题,因为编译器的行为没有很好的借口。相反,我会沉迷于疯狂的猜测,因为还没有答案。
我怀疑对齐算法中存在这样的错误:
如果存在此错误,我怀疑它是编译器早期作为 4 字节对齐 C 编译器遗留下来的。现在编译器的默认值是
/Zp8
,这意味着每个结构都至少对齐到 8 个字节,因此在这种情况下无论如何都不需要修复“第一个”成员的对齐方式。
问候, 谢尔姆