At a reply of a blog post of Raymond Chen,
提问者指出
雷蒙德,我相信自从这个位置以来C ++的例子是不正确的 未指定派生类中的基类子对象 根据ISO C ++ 2003标准(10-3,第168页),您可以假设 基类子对象始终在开头。 C 例子在C ++中也没问题,所以我坚持下去。
雷蒙德回答说
[代码没有做出这个假设。这就是为什么它很重要 使用static_cast而不是reinterpret_cast。试一试:添加虚拟 OVERLAPPED的方法(所以vtable在前面)并观察什么 编译器。 -Raymond]
读完他的评论后我猜错了。在示例中使用static_cast很好,但是reinterpret_cast不是。因为reinterpret_cast不能转换为vtable。我理解得对吗? 但是,如果我在那里使用C-Style演员(不是reinterpret_cast),它也会出错吗?
我重新阅读了更有效的C ++演员解释,以了解这一点。但是没有答案。
答案 0 :(得分:17)
在示例中使用static_cast很好但是reinterpret_cast不是。因为reinterpret_cast不能转换为vtable。
不,问题是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-Style演员(不是reinterpret_cast),它也会出错吗?
C样式演员被定义为以下第一个成功:
const_cast
static_cast
static_cast
,然后是const_cast
reinterpret_cast
reinterpret_cast
,然后是const_cast
正如您所看到的,在static_cast
之前尝试reinterpret_cast
,所以在这种情况下,C风格的演员也会做正确的事。
1 不保证。关于reinterpret_cast
上发生的事情几乎没有什么保证。我所知道的所有实现都会简单地给出相同的地址。