为什么在某些情况下reinterpret_cast有效,而在另一些情况下无效?

时间:2019-08-21 18:07:20

标签: c++ dynamic-cast reinterpret-cast

我刚刚开始与一个正在使用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中,重新解释强制转换为SEEMS可以正常工作似乎没有问题。 在第2种情况下,我注意到使用重新解释演员表时std :: vector的内部数据似乎已损坏

我的问题是为什么案例1通过?在std :: vector内是否应该存在数据损坏?

1 个答案:

答案 0 :(得分:6)

简短回答

问题是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)为真时才有效。这样您的代码才能正确运行,就像在注释中提到的那样,只是一个幸运的巧合。


更详细

D2的内存布局看起来像:

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

Base* b = new D2()将保存Base的地址(0x0000)。由于基类的属性始终存储在子类的属性之前,因此b(0x0000)中存储的地址与new D2()(0x0000)和{{1 }}将起作用。

但是另一方面,类reinterpret_cast的内存布局可能看起来像:

A

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

这就是case1有效而case2不正常的原因。


最后一件事:您永远不应该相信编译器始终使用相同的内存布局。关于内存布局,有很多事情没有在C ++标准中定义。 在此处使用reinterpret_cast会导致行为不确定!