投射到不同的 Base 类 会得到不同的结果。 C++
Casting to different Base classes gives different result. C++
也许我的问题不完整,但我的代码会让一切都清楚。
#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 是错误的设计。我不是在考虑设计,但行为才是困扰我的地方。
谁能解释一下为什么第一次使用不同的方式会产生不同的结果,而第二次会产生相同的结果??
本质上,C
的 A
和 B
部分不能占据相同的 space。一个必须先于另一个。当你正确地将 C*
转换为 A*
时,你会得到一个指向原始指针指向的实例的 A
部分的指针,转换为 [= 也是如此16=]。由于C
的A
部分(int A::n;
)和C
的B
部分(int B::n;
)必然在不同的地址,自然这些转换的结果也彼此不同。这是可能的,因为编译器可以知道 pc
指向的对象的布局,信息可以从它的类型中推断出来。如果信息不可用,这将不起作用,例如,如果指针首先被转换为 void*
。
reinterpret_cast
给出相同地址而不管您转换为什么的原因是因为 reinterpret_cast
就是这样做的。它将指针或引用转换为另一种类型,同时忽略任何形式的类型安全。 reinterpret_cast
指针是创建一个新类型的指针,其地址与提供的地址相同,而不管实际类型和类型安全性。
小心使用 reinterprect_cast
,因为它本质上是将事实注入类型安全系统。编译器一定会假设您告诉它的是正确的。如果那些 "facts" 不正确(例如 reinterpret_cast<B*>(pc)
的情况),您将面临未定义行为的风险。
定义一个class也意味着定义一个内存布局。在最简单的形式中,成员是连续排列的,例如
struct A {
int n;
};
在记忆中
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | n |
基数 classes
也是如此
struct C : A, B {
};
潜在的内存布局
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | A::n |
| 0042F830 | 4 | B::n |
现在您有一个指向 C
类型对象的指针 pc
。使用 static_cast
考虑了对象中成员和基 classes 的布局。
因此,您得到 A
或 B
部分的正确地址。
另一方面使用reinterpret_cast
,只是重用一个指针并假装它指向另一种类型。
Explanation
Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type.
这就是您获得相同地址值的原因。
也许我的问题不完整,但我的代码会让一切都清楚。
#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 是错误的设计。我不是在考虑设计,但行为才是困扰我的地方。 谁能解释一下为什么第一次使用不同的方式会产生不同的结果,而第二次会产生相同的结果??
本质上,C
的 A
和 B
部分不能占据相同的 space。一个必须先于另一个。当你正确地将 C*
转换为 A*
时,你会得到一个指向原始指针指向的实例的 A
部分的指针,转换为 [= 也是如此16=]。由于C
的A
部分(int A::n;
)和C
的B
部分(int B::n;
)必然在不同的地址,自然这些转换的结果也彼此不同。这是可能的,因为编译器可以知道 pc
指向的对象的布局,信息可以从它的类型中推断出来。如果信息不可用,这将不起作用,例如,如果指针首先被转换为 void*
。
reinterpret_cast
给出相同地址而不管您转换为什么的原因是因为 reinterpret_cast
就是这样做的。它将指针或引用转换为另一种类型,同时忽略任何形式的类型安全。 reinterpret_cast
指针是创建一个新类型的指针,其地址与提供的地址相同,而不管实际类型和类型安全性。
小心使用 reinterprect_cast
,因为它本质上是将事实注入类型安全系统。编译器一定会假设您告诉它的是正确的。如果那些 "facts" 不正确(例如 reinterpret_cast<B*>(pc)
的情况),您将面临未定义行为的风险。
定义一个class也意味着定义一个内存布局。在最简单的形式中,成员是连续排列的,例如
struct A {
int n;
};
在记忆中
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | n |
基数 classes
也是如此struct C : A, B {
};
潜在的内存布局
| Address | Size | Member |
|----------+------+--------+
| 0042F82C | 4 | A::n |
| 0042F830 | 4 | B::n |
现在您有一个指向 C
类型对象的指针 pc
。使用 static_cast
考虑了对象中成员和基 classes 的布局。
因此,您得到 A
或 B
部分的正确地址。
另一方面使用reinterpret_cast
,只是重用一个指针并假装它指向另一种类型。
Explanation
Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type.
这就是您获得相同地址值的原因。