为什么 reinterpret_cast 在某些情况下有效而在其他情况下无效?

Why does reinterpret_cast work in some cases but not others?

我刚开始与一个使用 reinterpret_cast 的团队合作,而显然它应该是 dynamic_cast。尽管他们使用 reinterpret_cast 代码似乎仍然可以正常工作,所以我决定不理会它,直到最近它最终停止工作。

struct Base {
 virtual void do_work() = 0;
};
struct D1 : public Base {
 virtual void do_work();
 std::vector<int> i;
};

struct D2: public D1 { 
 void do_work()
};

struct Holds_data {
    std::vector<int> i;
};
struct Use_data : public Holds_data {
 virtual void do_work();
};


struct A : public Use_data, public Base {
    void do_work();
};

//case 1
// this code works
Base* working = new D2();
D2*   d2_inst = reinterpret_cast<D2*>(working);


//case 2
Base* fail = new A();
A*    A_inst  = reinterpret_cast<A*>(fail); // fails
A*    A_inst  = dynamic_cast<A*>(fail);     // works

在情况 1 中似乎没有问题,重新解释 cast SEEMS 即可正常工作。 在案例 2 中,我注意到 std::vector 的内部数据在使用 reinterpret cast

时似乎已损坏

我的问题是案例 1 为什么会通过? std::vector 中不应该有数据损坏吗?

简答

问题是 Base* working = new D2(); 隐式地将 D2* 转换为 Base* (static_cast)。

所以如果你有:

D2* d2 = new D2();
Base* b = d2;

您无法确定 std::addressof(d2) == std::addressof(b) 是否为真。但是 reinterpret_cast 仅在 std::addressof(d2) == std::addressof(b) 为真时才有效。所以你的代码是 运行 正确就像在评论中提到的只是一个幸运的巧合。


更详细

class D2 的内存布局可能如下所示:

class D2:
0x0000 Attributes of Base
...
0x0010 Attirbutes of D1
...
0x0020 Attributes of D2
...

Base* b = new D2()会保存Base的地址(0x0000)。由于基classes的属性总是存储在子class的属性之前,因此b中存储的地址(0x0000)与[=24返回的地址相同=] (0x0000) 和 reinterpret_cast 将起作用。

但另一方面,class A 的内存布局可能如下所示:

class A:
0x0000 Attributes of HoldData
...
0x0010 Attributes of UserData
...
0x0020 Attributes of Base
...
0x0030 Attributes of A
...

这里编译器必须先存储UserDataBase的数据。因此,如果 UserData 首先存储(如示例中所示),Base* b = new A() 也将保存 Base (0x0020) 的地址,但由于 Base 不是第一个存储的A中的class,new A()返回的地址(0x0000)不等于b中保存的地址(0x0020),因为new A()(0x0000)是隐式静态转换为 Base*。这意味着 reinterpret_cast 将在此处失败。

这就是案例 1 有效而案例 2 无效的原因。


最后一件事:你永远不应该相信编译器它总是使用相同的内存布局。有很多关于内存布局的东西没有在标准中定义。 使用reinterpret_cast这里是未定义的行为!