为什么在这里使用static_cast而不是reinterpret_cast很重要?

9

在Raymond Chen的博客回复中,

一个提问者指出:

Raymond,我认为C++示例不正确,因为根据ISO C ++ 2003标准(10-3,第168页),派生类中基类子对象的位置是未指定的,并且您假设基类子对象始终位于开头。 C示例在C ++中也可以正常工作,因此我会坚持使用它。

Raymond回答道:

[代码并没有做出这种假设。这就是为什么使用static_cast而不是reinterpret_cast很重要的原因。试试吧:将一个虚方法添加到OVERLAPPED(所以vtable放在前面),并观察编译器的操作。 -Raymond]

我读完他的评论后猜测,使用static_cast在这个例子中是可以的,但是reinterpret_cast不行。因为reinterpret_cast不能转换vtable。我的理解正确吗?虽然,如果我在那里使用C风格的强制转换(而不是reinterpret_cast),也可能出错吗?
我重新阅读了《Effective C++》中关于类型转换的解释,但是没有找到答案。

2
我对C++一窍不通,但我的理解是,“reinterpret cast”在C语言中的意思就是*(destination_type *)&。可以想象,“static cast”实际上考虑了类之间的关系,并允许编译器进行非平凡的转换工作。 - R.. GitHub STOP HELPING ICE
1个回答

17
使用 static_cast 在这个例子中是可以的,但是 reinterpret_cast 不行。因为 reinterpret_cast 无法转换虚函数表。
不,问题在于 reinterpret_cast 完全忽略了继承关系。它只会简单地返回相同的地址1。但是 static_cast 知道你正在执行向下转型:即从一个基类到一个派生类的转换。由于它知道所涉及的两种类型,它会相应地调整地址,也就是做正确的事情。
让我们假设我们的实现布置了一个名为 OVERLAPPEDEX 的假想类,该类具有像这样的虚函数:
+------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------------+------------------+-------------+
       ^
       |
      ptr

我们得到的指针指向OVERLAPPED子对象。使用reinterpret_cast不会改变这一点,它只会改变类型。显然,通过这个地址访问OVERLAPPEDEX类会很容易造成混乱,因为它的子对象的位置现在都是错误的!
       what we believe we have when we access OVERLAPPEDEX through the pointer
       +------+------------+------------------+-------------+
       | vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------+-----+------+-----------+------+------+------+
| vptr | OVERLAPPED | AssociatedClient | ClientState | <- what we actually have
+------+------------+------------------+-------------+
       ^
       |
      ptr

static_cast知道将OVERLAPPED*转换为OVERLAPPEDEX*时必须调整地址,并且会执行正确的操作:

 +------+------------+------------------+-------------+
 | vptr | OVERLAPPED | AssociatedClient | ClientState |
 +------+------------+------------------+-------------+
 ^
 |
ptr after static_cast

尽管如此,在那里使用C风格转换(而不是reinterpret_cast),它也可能出错吗?
C风格转换被定义为以下第一个成功的转换之一:
1. const_cast 2. static_cast 3. static_cast,然后const_cast 4. reinterpret_cast 5. reinterpret_cast,然后const_cast
正如您所看到的,在尝试reinterpret_cast之前会尝试static_cast,因此在这种情况下,C风格转换也会做正确的事情。

更多信息


1不保证。在使用reinterpret_cast时,关于其发生的结果几乎没有任何保证。所有我所知道的实现都只是简单地返回相同的地址,不做任何修改。


1
@Benjamin 我添加了关于C风格转换的解释,以及为什么在这种情况下它可以工作。但我建议不要使用它们,因为并不总是清楚选择了哪个替代方案。C风格转换就像一把非常笨重的锤子。 - R. Martinho Fernandes
当然,我会遵循您的建议,因为我现在理解了。谢谢。不过,您的 C-Style 强制转换的顺序是标准化的吗? - Benjamin
1
@Benjamin 是的,这个顺序是由标准规定的。 - R. Martinho Fernandes
还要注意 C 风格的强制类型转换可能会出现一个问题:如果编译器无法确定存在继承结构(例如缺少头文件的包含),它将退回到 reinterpret_cast,以同样的方式失败。这是 C 强制类型转换危险的另一个原因。 - Mark B
我对你的脚注很好奇。标准允许reinterpret_cast改变值吗?那有什么用呢?这似乎与reinterpret_cast的设计相悖。 - StilesCrisis
显示剩余4条评论

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