乍一看,这看起来像未定义的行为......
#include <iostream>
struct SomeBaseClass
{
// ...
};
struct MyFakerClass
{
SomeBaseClass base;
void foo() { std::cout << "hello" << std::endl; }
};
int main()
{
MyFakerClass c;
MyFakerClass *p = static_cast<MyFakerClass *>(static_cast<void *>(&c.base));
p->foo();
}
...但是如果保证对象的第一个字段与对象具有相同的地址,那么这一定是安全的,对吧?还是有其他一些干预规则?
答案 0 :(得分:5)
指向标准布局结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),反之亦然。 [注意:因此,在标准布局结构对象中可能存在未命名的填充,但在开始时可能没有,以实现适当的对齐。 - 尾注]
(C ++ 11,§9.2,¶21)
因此,只要一个类是“标准布局”,即:
,这是安全的标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类
- 在大多数派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有包含非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类。
(C ++ 11,§9,¶7)
关于虚拟东西的所有要求都是因为vptr:如上所述,许多编译器把它作为第一个隐藏成员,但是允许他们把它放在任何地方,或者,如果他们能够,可以省略它或以其他方式实现虚拟调度,因此虚拟通常以未指定的方式改变类的布局。
“相同的访问控制条款”是因为
未指定具有不同访问控制的非静态数据成员的分配顺序。
(C ++ 11,§9.2,¶15)
我认为这可能是允许编译器通过访问控制对类的成员重新排序/分组(即在编译器内部,类的IR可能包含每个访问控制说明符下的成员列表,不允许保留原始排序 - 所有交错的公共/私有/受保护部分都将被分组,例如私有成员可以默认放在类前面。
no非标准布局成员/基类“递归”子句是为了确保该类是标准布局作为一个整体(基类和成员是它的一部分)。
有关其他内容可能影响课程布局的其他详细信息,请参见§9.2。
请注意,这是一个比POD类弱的条件(正如我在上面的注释中错误地说的那样),因为如果一个类是POD,如果它既是标准布局又是琐事(§9¶ 10)