何时使用reinterpret_cast?
我有点困惑reinterpret_cast
vs static_cast
的适用性。 从我读过的一般规则来看,当编译时可以解释types的时候,使用静态types转换( static
。 这是C ++编译器内部用于隐式强制转换的强制转换。
reinterpret_cast
s适用于两种情况,将整数types转换为指针types,反之亦然,或将一种指针types转换为另一种。 我得到的一般想法是不可移植的,应该避免。
我有点困惑的地方是我需要的一个用法,我从C调用C ++,C代码需要保持C ++对象,所以基本上它保留了一个void*
。 应该使用什么转换来在void *
和Classtypes之间进行转换?
我看到static_cast
和reinterpret_cast
用法? 虽然从我读过的东西看来, static
会更好,因为在编译时可以发生这种情况? 虽然它说使用reinterpret_cast
从一个指针types转换到另一个?
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
只保证,如果您将一个指针指向一个不同的types, 然后reinterpret_cast
它回到原来的types ,你会得到原来的价值。 所以在下面:
int* a = new int(); void* b = reinterpret_cast<void*>(a); int* c = reinterpret_cast<int*>(b);
a和c包含相同的值,但b的值未指定。 (在实际中,它通常包含与a和c相同的地址,但在标准中没有说明,在具有更复杂存储系统的机器上可能不是这样)。
对于void和void *, static_cast
应该是首选。
reinterpret_cast
有必要的一种情况是与不透明数据types进行交互。 这在程序员无法控制的供应商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
的含义不是由C ++标准定义的。 因此,理论上reinterpret_cast
可能会导致程序崩溃。 在实践中,编译器试图做你期望的事情,就是把你所传递的东西当作你所投射的types来解释。 如果你知道你将要使用的编译器与reinterpret_cast
做什么,你可以使用它,但是说它是可移植的将是撒谎。
对于你描述的情况,以及几乎任何你可能会考虑reinterpret_cast
,你可以使用static_cast
或者其他的替代方法。 除此之外,这个标准对于static_cast
(§5.2.9)有什么期望呢?
types“指向cv void的指针”右值可以显式转换为指向对象types的指针。 将指向对象的types指针的值转换为“指向cv void的指针”并返回到原始指针types将具有其原始值。
所以对于你的用例,标准化委员会打算使用static_cast
似乎很清楚。
简短的回答:如果您不知道reinterpret_cast
代表什么,请不要使用它。 如果将来需要它,你会知道的。
完整答案:
我们来考虑基本的数字types。
当你将int(12)
转换为unsigned float (12.0f)
处理器需要调用一些计算,因为两个数字都有不同的位表示。 这就是static_cast
代表的意思。
另一方面,当你调用reinterpret_cast
,CPU不会调用任何计算。 它只是在内存中处理一些位,就像它有另一种types一样。 所以当你用这个关键字将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
。
reinterpret_cast的一个用途是如果您想要将位操作应用于(IEEE 754)浮点数。 其中一个例子就是快速逆向平方根技巧:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
它将浮点数的二进制表示法看作一个整数,将其右移并将其从常量中减去,从而将指数减半并取反。 在转换回浮点数后,经过牛顿 – 拉夫逊迭代,使这个近似更准确:
float Q_rsqrt( float number ) { long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &y; // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the deuce? y = * ( float * ) &i; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; }
这最初是用C语言编写的,所以使用C语言转换,但类似的C ++语言转换是reinterpret_cast。
您可以使用reinterprete_cast在编译时检查inheritance。
看看这里: 使用reinterpret_cast在编译时检查inheritance
template <class outType, class inType> outType safe_cast(inType pointer) { void* temp = static_cast<void*>(pointer); return static_cast<outType>(temp); }
我试图用模板来结束和写一个简单的安全演员。 请注意,此解决scheme不保证在函数上投射指针。
首先你有一些特定types的数据,比如int:
int x = 0x7fffffff://==nan in binary representation
然后你想访问像float这样的其他types的variables:你可以决定
float y = reinterpret_cast<float&>(x); //this could only be used in cpp, looks like a function with template-parameters
要么
float y = *(float*)&(x); //this could be used in c and cpp
简单说:这意味着相同的内存被用作不同的types。 所以你可以将浮点数的二进制表示forms转换为像上面那样的inttypes的浮点数。 例如0x80000000是-0(尾数和指数都是空的,但符号,即MSB是1,这也适用于双打和长双打。
优化:我认为reinterpret_cast将在许多编译器中进行优化,而c-cast是由pointerar arithmetic(值必须复制到内存,导致指针不能指向CPU寄存器)。
注意:在这两种情况下,您应该在铸造前将铸造值保存在variables中! 这个macros可以帮助:
#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
快速回答:如果编译时使用static_cast
,否则请使用reinterpret_cast
。
阅读常见问题 ! 在C中保存C ++数据可能是有风险的。
在C ++中,一个指向一个对象的指针可以被转换为void *
而不需要任何强制转换。 但相反的事实并非如此。 你需要一个static_cast
来获取原始指针。