将对象的第一个字段强制转换为对象的类型是否真的安全?
Is casting the first field of an object to the object's type actually safe?
乍一看,这看起来像是未定义的行为。。。
#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();
}
但是,如果一个对象的第一个字段保证与该对象具有相同的地址,那么这一定是安全的,对吧?还是有其他规则介入?
指向标准布局结构对象的指针,使用interpret_cast进行适当转换,指向其初始成员(或者,如果该成员是位字段,则指向其所在的单元),反之亦然[注意:因此,标准布局结构对象中可能有未命名的填充,但不是在其开头,这是实现适当对齐所必需的。--结束注释]
(C++11,§9.2,¶21)
因此,只要一个类是"标准布局",即:,这是安全的
标准布局类是指:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员
- 没有虚拟函数(10.3),也没有虚拟基类(10.1)
- 对所有非静态数据成员具有相同的访问控制(条款11)
- 没有非标准布局基类
- 在最派生的类中没有非静态数据成员,并且最多有一个基类具有非静态数据会员,或者没有基类具有非静止数据会员,以及
- 没有与第一个非静态数据成员类型相同的基类
(C++11,§9,¶7)
关于虚拟内容的所有要求都是因为vptr:如上所述,许多编译器将其作为第一个隐藏成员,但他们可以将其放在任何地方,或者如果可以的话,可以完全省略它,或者以其他方式实现虚拟调度,因此virtual通常会以未指定的方式更改类的布局。
"相同的访问控制条款"是因为
具有不同访问控制的非静态数据成员的分配顺序未指定。
(C++11,§9.2,¶15)
我想这可能是为了允许编译器通过访问控制对类的成员进行重新排序/分组(即,在编译器内部,类的IR可能包含每个访问控制说明符下的成员列表,这不允许保留原始顺序-所有交错的公共/私有/受保护部分都将被分组,例如,默认情况下,私有成员可能会放在类的前面)。
无非标准布局成员/基类的"递归"子句是为了确保类作为一个整体是标准布局(基类和成员是其中的一部分)。
关于为什么这些东西的其余部分会影响类的布局的更多细节可以在§9.2中找到。
注意,这是一个比类是POD弱的条件(正如我在上面的评论中错误地说的),因为如果一个类既是标准布局又是琐碎的(§9¶10),那么它就是POD
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 在这种情况下,java对象是否可以调用本机函数
- 堆分配的对象是否存在永不为空的唯一所有者?
- QFileSystemModel 对象是否会被删除?
- 具有相同特征的两个对象是否只在内存中存储一次?无论定义它们的函数是什么,都是不同的
- 内联函数的函数本地静态对象是否在共享对象文件之间共享?
- 临时C++对象是否为左值?
- 为什么复制构造函数不需要检查输入对象是否指向自身?
- "this"指向的对象是否与 const 对象相同?
- 有什么方法可以检测我的类的对象是否在堆栈上创建
- 基类对象是否隐式添加到派生类中?
- 当 T 具有非平凡析构函数时,类类型 T 的对象是否可以常量初始化?
- 如何检查指针后面的对象是否有效或已删除?
- 谷神星求解器:残差函子使用的可变对象是否良好实践?还有什么其他选择
- 单一实例对象是否通过线程安全返回shared_ptr
- 对象是否保证在调用其成员函数之前被初始化
- 在另一个对象 B 中创建对象 A 时,对象 A 是否是对象 B 的本地对象,对象 A 是否会存在于对象 B 的实例化之外?
- 如何确定对象是否已分配成员
- 我们如何在c 中序列化或应对类的对象.是否有任何预定义的库
- 友元类对象是否可以在其成员函数中访问派生类对象的基类私有成员?