我对reinterpret_cast和static_cast的适用性有点困惑。根据我所读到的,一般规则是当类型可以在编译时解释时使用静态强制转换,因此是静态这个词。这也是c++编译器内部用于隐式类型转换的类型转换。
reinterpret_cast适用于以下两种情况:
将整数类型转换为指针类型,反之亦然
将一种指针类型转换为另一种。我得到的一般想法是,这是不可移植的,应该避免。
我有点困惑的地方是我需要的一种用法,我从C调用c++, C代码需要保持c++对象,所以基本上它持有一个void*。在void *和Class类型之间应该使用什么类型转换?
我已经看到使用static_cast和reinterpret_cast?虽然从我所读到的似乎静态更好,因为强制转换可以在编译时发生?虽然它说使用reinterpret_cast从一种指针类型转换为另一种?
c++标准保证了以下内容:
指向void*和来自void*的Static_casting指针将保留地址。也就是说,下面的a、b、c都指向同一个地址:
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
Reinterpret_cast只保证如果将一个指针强制转换为不同的类型,然后将其重新转换为原始类型,则得到原始值。下面是:
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的一个用途是如果你想对(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。
需要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的含义不是由c++标准定义的。因此,理论上reinterpret_cast可能导致程序崩溃。在实践中,编译器试图做你所期望的事情,也就是解释你传入的二进制位,就好像它们是你要强制转换的类型一样。如果你知道你将要使用的编译器对reinterpret_cast做了什么,你就可以使用它,但是说它是可移植的是在撒谎。
对于您所描述的情况,以及您可能会考虑reinterpret_cast的大多数情况,您可以使用static_cast或其他替代方法。在其他事情中,标准有这样说你可以期待static_cast(§5.2.9):
类型为“指向cv void的指针”的右值可以显式转换为指向对象类型的指针。一个类型为pointer to object的值转换为" pointer to cv void "并返回到原始指针类型将有其原始值。
因此,对于您的用例,标准化委员会显然希望您使用static_cast。