何时使用重新解释
我对reinterpret_cast
vs static_cast
的适用性感到困惑。 从我读过的一般规则来看,当编译时可以解释类型时使用静态转换,因此使用static
。 这是C ++编译器内部用于隐式强制转换的强制转换。
reinterpret_cast
s适用于两种情况,将整数类型转换为指针类型,反之亦然,或将一种指针类型转换为另一种类型。 我得到的一般想法是不可移植的,应该避免。
我有点困惑的地方是我需要的一种用法,我从C调用C ++,C代码需要保持C ++对象,所以它基本上保留了void*
。 应该使用什么转换来在void *
和Class类型之间进行转换?
我看到static_cast
和reinterpret_cast
用法了? 尽管从我一直在阅读的内容来看, static
效果更好,因为在编译时可以发生剧情? 虽然它说使用reinterpret_cast
从一种指针类型转换到另一种指针类型?
C ++标准保证了以下内容:
static_cast
一个指向void*
的指针保留地址。 也就是说,在下面,a,b和c都指向相同的地址:
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
reinterpret_cast
只保证,如果你将一个指针指向一个不同的类型,然后reinterpret_cast
它reinterpret_cast
回原来的类型,你会得到原始值。 所以在下面:
int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);
a和c包含相同的值,但b的值未指定。 (在实际中,它通常包含与a和c相同的地址,但标准中没有说明这一点,在具有更复杂存储系统的机器上可能不是这样)。
对于从void *进行投射, static_cast
应该是首选。
reinterpret_cast
有必要的一种情况是与不透明数据类型进行交互。 这经常发生在程序员无法控制的供应商API中。 以下是供应商提供用于存储和检索任意全局数据的API的一个人为的示例:
// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();
要使用此API,程序员必须将其数据转换为VendorGlobalUserData
并返回。 static_cast
不起作用,必须使用reinterpret_cast
:
// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;
struct MyUserData {
MyUserData() : m(42) {}
int m;
};
int main() {
MyUserData u;
// store global data
VendorGlobalUserData d1;
// d1 = &u; // compile error
// d1 = static_cast<VendorGlobalUserData>(&u); // compile error
d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok
VendorSetUserData(d1);
// do other stuff...
// retrieve global data
VendorGlobalUserData d2 = VendorGetUserData();
MyUserData * p = 0;
// p = d2; // compile error
// p = static_cast<MyUserData *>(d2); // compile error
p = reinterpret_cast<MyUserData *>(d2); // ok
if (p) { cout << p->m << endl; }
return 0;
}
以下是示例API的设计实现:
// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
简短的回答:如果您不知道reinterpret_cast
代表什么,请不要使用它。 如果将来需要它,你会知道的。
完整答案:
我们来考虑基本的数字类型。
当你将int(12)
转换为unsigned float (12.0f)
你的处理器需要调用一些计算,因为两个数字都有不同的位表示。 这就是static_cast
代表的意思。
另一方面,当你调用reinterpret_cast
,CPU不会调用任何计算。 它只处理内存中的一组位,就像它有另一种类型一样。 所以当你用这个关键字将int*
转换为float*
时,新的值(在取消指针后)与数学意义上的旧值没有任何关系。
例子: reinterpret_cast
确实不可移植,因为一个原因 - 字节顺序(字节顺序)。 但这通常是令人惊讶的使用它的最好理由。 让我们想象一下这个例子:你必须从文件中读取二进制的32位数字,并且你知道它是大端的。 您的代码必须是通用的,并且可以在大端(如ARM)和小端(如x86)系统上正常工作。 所以你必须检查字节顺序。 它在编译时很有名,所以你可以编写constexpr
函数:
constexpr bool is_little_endian() {
unsigned short x=0x0001;
auto p = reinterpret_cast<unsigned char*>(&x);
return *p != 0;
}
说明:内存中x
的二进制表示可能是0000'0000'0000'0001
(大)或0000'0001'0000'0000
(小端)。 重新0000'0000
, p
指针下的字节可以分别为0000'0000
或0000'0001
。 如果您使用静态铸造,无论使用哪种字节顺序,它总是为0000'0001
。