3

乍一看,这看起来像是未定义的行为......

#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();
}

...但是如果保证对象的第一个字段与对象具有相同的地址,那么这一定是安全的,对吧?还是有其他规则介入?

4

1 回答 1

5

指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。[注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。——尾注]

(C++11,§9.2,¶21)

因此,只要类是“标准布局”,这是安全的,即:

标准布局类是这样的类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数 (10.3) 和虚基类 (10.1),
  • 对所有非静态数据成员具有相同的访问控制(第 11 条),
  • 没有非标准布局的基类,
  • 要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类。

(C++11,§9,¶7)

所有关于虚拟的东西的要求都是因为 vptr:如上所述,许多编译器把它作为第一个隐藏成员,但是他们可以把它放在任何地方,或者,如果他们能够,完全省略它或实现 virtual以其他方式分派,因此virtual通常以未指定的方式改变类的布局。

“相同的访问控制条款”是因为

未指定具有不同访问控制的非静态数据成员的分配顺序。

(C++11,§9.2,¶15)

我想这可能是为了允许编译器通过访问控制对类的成员进行重新排序/分组(即在编译器内部,类的 IR 可能包含每个访问控制说明符下的成员列表,这不允许保持原始顺序 - 所有交错的公共/私有/受保护部分都将被分组,例如默认情况下私有成员可能放在类的前面)。

没有非标准布局成员/基类“递归”子句是为了确保该类作为一个整体是标准布局(基类和成员是它的一部分)。

关于为什么这些东西的其余部分会影响类的布局的更多细节可以在 §9.2 中找到。


请注意,这是比作为 POD 的类更弱的条件(正如我在上面的评论中错误地所说),因为如果一个类既是标准布局又是微不足道的(§9 ¶10) ,它就是 POD

于 2012-12-09T23:15:08.107 回答