C++标准规定,单个访问部分中的成员变量必须按照它们声明的顺序在内存中排列。同时,编译器可以自由选择访问部分之间的相互顺序。这种自由性理论上使得无法链接由不同编译器创建的二进制文件。那么,严格的部分内排序仍有哪些原因呢?而且,即将到来的C++09 新的C++11标准是否提供了一种“手动”完全确定对象布局的方式?
C++标准规定,单个访问部分中的成员变量必须按照它们声明的顺序在内存中排列。同时,编译器可以自由选择访问部分之间的相互顺序。这种自由性理论上使得无法链接由不同编译器创建的二进制文件。那么,严格的部分内排序仍有哪些原因呢?而且,即将到来的C++09 新的C++11标准是否提供了一种“手动”完全确定对象布局的方式?
operator new
和delete
的实现、数据类型大小等等。class
或struct
,如果使用[u]int[8|16|32|64]_t
类型,则已经可以实现。你需要比这更多的用例吗?[编辑] 今天我学到了新东西!找到了以下标准引用:
未经访问说明符声明的(非联合)类的非静态数据成员分配在类对象内,以便后续成员在类对象内具有更高的地址。由访问说明符分隔的非静态数据成员的分配顺序是未指定的(11.1)。实现对齐需求可能导致两个相邻的成员不会立即分配在一起;管理虚拟函数(10.3)和虚拟基类(10.1)的空间也可能导致这种情况。
有趣 - 我不知道为什么要给予这种程度的自由。继续我的先前的回复...
如上所述,保留排序的原因是为兼容C,并且当时我认为没有人想到重新排列成员的好处,而且内存布局通常是手工完成的。此外,现在被认为是“丑陋技巧”的东西(例如使用memset零化选定的成员或具有相同布局的两个结构体)曾经很常见。
标准不提供强制执行给定布局的方法,但大多数编译器提供控制填充的措施,例如在MSVC编译器上使用#pragma pack。
自动填充的原因是平台可移植性: 不同的架构具有不同的对齐要求,例如一些架构在错位的整数上抛出异常(那时这些情况是简单的)。
你永远不应该链接由不同编译器创建的对象。即使你所说的内容发生了改变,你仍然会遇到更多的问题,阻止你与另一个编译器生成的文件进行链接(例如对齐、名称混淆、调用约定等等)。
编译器自由地对访问部分进行排序的一个原因可能是为了建立访问部分的顺序:具有较低地址的成员比具有较高地址的成员更受保护,例如。
如果不允许重新排序,你将不会获得任何好处:只有POD提供C兼容性并提供一种方法来给出类/结构体中成员的字节偏移量(使用宏offsetof
)或允许你对它们进行memcpy。如果定义了自定义构造函数、复制构造函数、私有成员或其他一些东西,类型将变为非POD。特别是,从类派生目前会破坏PODness。
C++1x降低了POD的要求。例如,在C++1x中,std::pair<T, U>
实际上是一个POD,即使它提供了自己的构造函数(虽然必须符合某些规则)。
编辑:恐怕我误解了你的问题
我认为这是一种内存访问优化。 例如,如果我们有这个结构:
struct example
{
int16 intData;
byte byteData;
int32 intData;
byte intData;
int32 intData;
}
假设此平台上的字长为32位。然后,你需要4个完整的字来传递结构中的所有数据:
int16 + byte = 24位(下一个字段不适合在此处)
int32 = 32位(下一个字段不适合在此处)
byte = 8位(下一个字段不适合在此处)
int32 = 32位
但如果你重新排列字段:
struct example
{
int16 intData;
byte byteData;
byte intData;
int32 intData;
int32 intData;
}
那么你就可以节省一次内存访问。