也许我的问题并不完美,但我的代码会让一切都清楚。
#include <iostream>
using namespace std;
struct A{int n;};
struct B{int n;};
struct C : A, B{};
int main()
{
C c;
C* pc = &c;
std::cout<<"TEST1"<<std::endl;
cout << static_cast<B*>(pc) << "\n";
cout << reinterpret_cast<B*>(pc)<<"\n\n";
std::cout<<"TEST2"<<std::endl;
cout << static_cast<A*>(pc) << "\n";
cout << reinterpret_cast<A*>(pc)<<"\n";
}
输出是:
TEST1
0042F830
0042F82C
TEST2
0042F82C
0042F82C
我知道使用reinterpret_cast是错误的设计。我不是在考虑设计,但行为是困扰我的。 任何人都可以解释为什么不同的方式会在第一次给出不同的结果但第二次会产生相同的结果吗?
答案 0 :(得分:3)
基本上,A
的{{1}}和B
部分不能占用相同的空间。一个人必须先到另一个人面前。当您正确地将C
强制转换为C*
时,您将获得指向原始指针所指向的实例的A*
部分的指针,并且对于转换为{{ 1}}。由于A
(B*
)的A
部分和C
(int A::n;
)的B
部分必然位于不同的地址,因此&# 39;自然而然,这些转换的结果也彼此不同。这是可能的,因为编译器可以知道C
指向的对象的布局,该信息可以从它的类型中推导出来。如果信息不可用,这将无法工作,例如,如果指针首先被转换为int B::n;
。
pc
提供相同地址的原因,无论您投放的是什么,都是因为void*
具体是reinterpret_cast
。它将指针或引用转换为另一种类型,同时忽略任何形式的类型安全性。要reinterpret_cast
,指针就是创建一个新类型的指针,其地址与提供的地址相同,无论实际类型和类型安全如何。
谨防使用reinterpret_cast
,因为它基本上将事实注入到类型安全系统中。编译器必然会假设您所说的是正确的。如果那些&#34;事实&#34;不是真的(就像reinterprect_cast
的情况一样),你冒着未定义的行为风险。
答案 1 :(得分:2)
定义类也意味着定义内存布局。最简单的形式是成员连续布局,例如
struct A {
int n;
};
并且在记忆中
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | n |
基类
也是如此struct C : A, B {
};
潜在的内存布局
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | A::n |
| 0042F830 | 4 | B::n |
现在,您有一个pc
类型对象的指针C
。使用static_cast
会考虑对象中成员和基类的布局。
因此,您可以获得A
或B
部分的正确地址。
在另一边使用reinterpret_cast
,只需重复使用指针并假装它指向另一种类型。
<强>解释强>
与static_cast不同,但与const_cast类似,reinterpret_cast表达式不会编译为任何CPU指令。它纯粹是一个编译器指令,它指示编译器将表达式的位序列(对象表示)视为具有new_type类型。
这就是为什么你得到相同的地址值的原因。