〈一〉开门见山我们先通过两个例子对比了解一下reinterpret_cast(32位操作系统为例)
1〉
#include <iostream> using namespace std; int main() { int myArray[5]={0,1,2,3,4}; char myChar[5]={'H','a','o','Y','u'}; int *aPoint = myArray; char *bPoint = myChar; for(int i=0;i<5;i++) { cout<<*(aPoint++) <<"----------------" <<reinterpret_cast<unsigned long>(aPoint)<<endl;; } cout <<"-------------------------"<<endl; for(i=0;i<5;i++) { cout<<*(bPoint++) <<"-------"<<reinterpret_cast<unsigned long>(bPoint)<<endl; } return 0; }
运行结果如下:
2〉
#include <iostream> using namespace std; int main() { int myArray[5]={0,1,2,3,4}; char myChar[5]={'H','a','o','Y','u'}; int *aPoint = myArray; char *bPoint = myChar; for(int i=0;i<5;i++) { // cout<<*(aPoint++) <<"--------" <<reinterpret_cast<unsigned long>(aPoint)<<endl;; cout<<*(aPoint++) <<"--------" <<(aPoint)<<endl;; } cout <<"-------------------------"<<endl; for(i=0;i<5;i++) { cout.clear(); // cout<<*(bPoint++) <<"-------"<<reinterpret_cast<unsigned long>(bPoint)<<endl; cout<<*(bPoint++) <<"-------"<<(bPoint)<<endl; } return 0; }
运行结果为:\
以上结果有什么不同??原因是什么?不着急请继续往下看!!
我们由浅入深进行研究reinterpret_cast:
reinterpret_cast是C++里的强制类型转换符。
操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。
例如:int *n= new int ;
double *d=reinterpret_cast<double*> (n);
在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析。
因此, 需要谨慎使用 reinterpret_cast.
并且:reinterpret_cast 只能在指针之间转换。
== ===========================================
== static_cast .vs. reinterpret_cast
== ================================================
reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话)
reinterpret_cast <new_type> (expression)
reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。
什么是无关类型?我没有弄清楚,没有找到好的文档来说明类型之间到底都有些什么关系(除了类的继承以外)。后半句倒是看出了reinterpret_cast的字面意思:重新解释(类型的比特位)。我们真的可以随意将一个类型值的比特位交给另一个类型作为它的值吗?其实不然。
IBM的C++指南里倒是明确告诉了我们reinterpret_cast可以,或者说应该在什么地方用来作为转换运算符:
- 从指针类型到一个足够大的整数类型
- 从整数类型或者枚举类型到指针类型
- 从一个指向函数的指针到另一个不同类型的指向函数的指针
- 从一个指向对象的指针到另一个不同类型的指向对象的指针
- 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
- 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针
reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。
(所谓"足够大的整数类型",取决于操作系统的参数,如果是32位的操作系统,就需要整形(int)以上的;如果是64位的操作系统,则至少需要长整形(long)。具体大小可以通过sizeof运算符来查看)。
MSDN的Visual C++ Developer Center 给出了它的使用价值:用来辅助哈希函数。下边是MSNDN上的例子:
#include <iostream> using namespace std; unsigned short Hash(void *p) { unsigned int val = reinterpret_cast<unsigned long>(p); return (unsigned short)(val ^ (val>>16)); } int main() { int a[20]; for(int i=0;i<20;i++) { cout<<Hash(a+i)<<endl; } return 0; }
运行结果如下:
这段代码适合体现哈希的思想,暂时不做深究,但至少看Hash函数里面的操作,也能体会到,对整数的操作显然要对地址操作更方便。在集合中存放整形数值,也要比存放地址更具有扩展性(当然如果存void *扩展性也是一样很高的),唯一损失的可能就是存取的时候整形和地址的转换(这完全可以忽略不计)。
〈二〉我们仍然先通过几个例子进行说明:
#include <iostream> using namespace std; int main() { float f = 12.3f; float *pf = &f; int n = static_cast<float>(f); //成功编译,运行结果12 cout <<"n="<< n<<endl; //int *pn = static_cast<int>(pf); //错误,指向的类型是无关的(译注:即指针变量pf是float类型,现在要被转换为int类型) //cout <<"pf="<<pf<<endl; void *pv = static_cast<void *>(pf);//编译成功 cout <<"pv地址"<<pv<<endl; int *pn2 = static_cast<int *>(pv); cout <<"pn2地址"<<pn2<<endl; //编译成功,通过运行结果可知,pv和pn2的地址相同! return 0; }
运行结果:
#include <iostream> using namespace std; class CBaseY { public: int y; int *py; CBaseY() { y=20; py=&y; } void bar() { cout <<"-------y = "<<y<<endl; cout <<"*py="<<*py<<endl; } }; class CBaseX { public: int x; CBaseX() { x = 10; } void foo() { cout <<"-------x = "<<x<<endl; } }; class CDerived:public CBaseX,CBaseY { public: int z; }; int main() { CBaseX *pX = new CBaseX(); pX->foo(); //运行结果 x=10; //CBaseY *pY1 = static_cast<CBaseY *>(pX);//编译错误,类型指向是无关的! CBaseY *pY2 = reinterpret_cast<CBaseY *>(pX);//编译成功,运行结果x=10,y=10 // pY2->bar(); //系统在运行过程中会产生中断!!导致系统奔溃! CBaseY *pY = new CBaseY(); CBaseX *pX2 = reinterpret_cast<CBaseX *>(pY); pX2->foo(); //编译成功,运行结果x=10,x=20; }
总结:正如我们在泛型例子中所认识到的,如果你尝试转换一个对象到另一个无关的类static_cast<>将失败,而reinterpret_cast<>就总是成功“欺骗”编译器:那个对象就是那个无关类。