char* 转换和别名规则

5

根据严格别名规则:

struct B { virtual ~B() {} };
struct D : public B { };

D d;
char *c = reinterpret_cast<char*>(&d);

任何类型的对象都可以转换成char*,但现在的问题是,它是否会指向&d的相同地址?C++标准保证它将返回相同的地址。

9
我认为你的析构函数命名不正确。 - Seth Carnegie
4
我不知道答案。但是,这个知识何时会在实际中有用呢?其中的“ever”表示强调。 - Oliver Charlesworth
好问题。有些强制转换实际上可以改变地址(例如涉及多重继承时)。我想知道这是否是这种情况。 - Kos
“相同地址”是什么意思?应该什么等于什么? - n. m.
问题在哪里?(你无法解决的问题) - Qchmqs
显示剩余2条评论
3个回答

6
c&d 确实具有相同的值,如果你重新解释将 c 转换为 D*,你会得到一个有效的指针,可以对其进行解引用。此外,你可以将 c 视为(指向数组 char[sizeof(D)] 的第一个元素的)不透明数组 - 这确实是将指针转换为 char 指针的主要目的:允许序列化和反序列化(例如 ofile.write(c, sizeof(D));),但通常只应对基本类型(及其数组)执行此操作,因为复合类型的二进制布局通常无法以可移植的方式指定。
正如 @Oli 正确指出并希望我加强的那样,你真的永远不应该将复合类型作为整体进行序列化。结果几乎永远无法进行反序列化,因为多态类和数据字段之间的填充的实现未指定且不可访问。
请注意,通过类似的推理,reinterpret_cast<char*>(static_cast<B*>(&d)) 可以被视为一个不透明数组 char[sizeof(B)]

我认为你可能应该更清楚地表明,像这样序列化/反序列化非POD类(即带有虚函数指针的类)是一个非常非常糟糕的想法。 - Oliver Charlesworth
@OliCharlesworth: 嗯,对象没有任何隐藏的“vptrs”...这都在类实现中。对象应该仍然具有相当“正常”的布局,至少直到你进入虚拟继承的领域...通常的填充问题是不序列化类类型的“天真”方式的一个更为直接的原因(尽管也许有一些情况是可以的,例如,使用内存映射网络I/O的相同机器的HPC集群...)。买家自负 :-) - Kerrek SB
什么?虚函数的存在意味着类实例必须有一个vptr(在任何典型的实现中)。 - Oliver Charlesworth
@OliCharlesworth:嗯,我可能对此有一个错误的想法...我一直认为每个只有一个vtable。你所需要的就是一个实例指针来调用成员函数,甚至是虚函数... - Kerrek SB
确实,该类包含虚函数表。但是每个实例都包含一个指向虚函数表的虚函数指针。 - Oliver Charlesworth
显示剩余2条评论

2
2003年C++标准的第5.2.10节第7点规定:可以将对象的指针显式转换为不同类型的对象的指针。除了将类型为“指向T1”的rvalue转换为类型为“指向T2”的指针(其中T1和T2是对象类型,且T2的对齐要求不比T1更严格)并返回其原始类型指针值,此类指针转换的结果未指定。如果你所说的“相同地址”是指“原始指针值”,那么这个条目就是“是”。

0

意图很明确(不需要争论):

reinterpret_cast 永远不会改变地址的值,除非目标类型不能表示所有地址值(例如小整数类型,在具有内在对齐的指针类型上:例如只能表示偶地址的指针,或者对象指针和函数指针不能混合使用...)。

标准措辞未能捕捉到这一点,但这并不意味着这里存在真正的实际问题。

char *c = reinterpret_cast<char*>(&d);

c 将始终指向 d 的第一个字节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接