为什么 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
...
这里编译器必须先存储UserData
或Base
的数据。因此,如果 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
这里是未定义的行为!
我刚开始与一个使用 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
...
这里编译器必须先存储UserData
或Base
的数据。因此,如果 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
这里是未定义的行为!