有时,C ++编译器会为同一个二进制文件中的相同类型T生成不同的内存布局。也就是说,当对象同时作为类的非连续子对象以及作为独立对象或数组子对象出现时,会发生这种情况:
struct A { int i; };
struct B : virtual A { int i; };
struct C : virtual A { int i; };
struct D : B,C { int i; };
...
D d;
B b;
B* p1= &(B&)d;
B* p2= &b;
根据C ++ 14标准,编译器可以自由地为单个二进制文件中的T类对象生成任意数量的不同布局吗? 内存布局是否在编译时固定?
分拆:How does placement new know which layout to create?
第二个问题的详述: 类型T的对象t可以通过T *或char *指针访问。 (后者由§3.10(10)证明合理)一旦编译的程序运行,可以通过通过char指针访问t来确定t的子对象的相对偏移。这些偏移是否具有确定性,或者它们是否可以从一个程序执行变为另一个程序?
答案 0 :(得分:2)
根据C ++ 14标准,如果将指针转换为指向其他类的指针然后使用指针,则会获得UB。所以你不能从类开始偏移,然后添加它的类指针。
对于标准布局对象,您可以获得更多保证,并且可以使用offsetof并获得确定性结果。具有虚拟继承的类是棘手的,绝对不是标准布局。
答案 1 :(得分:0)
初步答复(截至目前,不要认为这是理所当然的):
假设T是具有至少两个非静态数据成员的类类型。如果T不是标准布局类,我们假设这两个数据成员是公共的和私有的,具有不同的访问控制。可以理解,下面描述的布局符合标准规定的排序和连续性要求。
a)如果T既不是可复制的也不是标准布局,则当创建类型为T的对象时,实现可以自由地使用随机数生成器来创建新的存储器布局。 T可以由单个二进制文件创建的不同内存布局的数量没有上限。 (标准中没有相反的东西。真的吗?一定是遗漏了什么)
b)如果T是平凡可复制的,而不是标准布局,则实现可以在程序启动时自由使用随机数生成器来生成T的布局。可能的布局数是有限的。布局可能因程序执行不同而不同。 (由于§3.9.2和§1.8.5的限制)
c)如果T是标准布局,则只有一种可能的布局。 (保证订单和邻接)