阅读webkit代码,遭遇C++显示转换,复习下。
过去几年,我更多的是在同C语言打交道,在C语言中,强制类型转换非常简单。
double pi=3.14;
int piInt=(int)pi;
这个强制类型转换会丢失部分数据(.14),所以如果不加(int)做强制转换,严检查的编译会报错,宽检查的编译会报warning。
在C语言中,指针是4字节或者8字节的,所以指针之间的强制转换在转换的时候就如同不同的int之间的赋值,问题不大,问题在于对该指针的使用上,必须确保该指针确实可以做出这样的强制转换。常见的情况是void*到不同的指针类型(比如内存分配,参数传递),char*和unsigned char*这样的转换。也有在读文件的时候,直接把某个结构映射为内存,写文件的时候,把某块内存直接映射成结构体。
标准C++提供了更好的转换方法,一种无论对程序员和代码解析器都有利的方式。
标准C++中主要有4种显示类型转换类型运算符:reinterpret_cast,static_cast,const_cast,dynamic_cast。
语法:
returnvalue=reinterpret_cast(casting value);
这个操作符修改了操作数类型,但仅仅是重新解释了对象的比特模型而没有进行二进制转换。
从语法上看,这个操作符仅用于指针类型的转换(返回值是指针)。它用来将一个类型指针转换为另一个类型指针,它只需在编译时重新解释指针的类型。
这个操作符基本不考虑转换类型之间是否是相关的。
reinterpret_cast的本质(http://blog.csdn.net/coding_hello/archive/2008/03/24/2211466.aspx)一文做的试验很好的解释了reinterpret_cast不做二进制转换的特点。我喜欢从C语言的角度来理解这个操作符,就像C语言中的指针强制转换,其实只是把地址赋给了新的指针,其它的不做改变,只在新的指针使用的时候,进行不一样的解释。看如下的例子:
#include using namespace std; class classA { public: int valueX; int valueY; classA() { valueX = 0; valueY = 0;} }; int main() { classA *a = new classA(); a->valueX = 10; a->valueY = 30; cout << "Value of X = " << a->valueX << " Value of Y = " << a->valueY << endl; void *aClassVoid = reinterpret_cast<void*>(a); a = reinterpret_cast(aClassVoid); cout << "COME BACK To me !! Value of X = " << a->valueX << " Value of Y = " << a->valueY << endl; cout << "Value of X = " << a->valueX << " Value of Y = " << a->valueY << endl; aClassVoid = reinterpret_cast<void*>(a); // but if you alter the values within a variable once you have done the first cast. cout << "After the first cast .. Value of X = " << a->valueX << " Value of Y = " << a->valueY << endl; a->valueX = 0; cout << "After settings the value to 0 .. Value of X = " << a->valueX << " Value of Y = " << a->valueY << endl; //and try again with the casting with the aClassVoid classA *AP = reinterpret_cast(aClassVoid); cout << "COME BACK To me !! Value of X = " << AP->valueX << " Value of Y = " << AP->valueY << endl; cout << "The last reinterpret_cast leaves the value as 0 for valueX because it is still only pointing to the same place as 'a'" << endl; return 0; }
输出结果:
Value of X = 10 Value of Y = 30
COME BACK To me !! Value of X = 10 Value of Y = 30
Value of X = 10 Value of Y = 30
After the first cast .. Value of X = 10 Value of Y = 30
After settings the value to 0 .. Value of X = 0 Value of Y = 30
COME BACK To me !! Value of X = 0 Value of Y = 30
The last reinterpret_cast leaves the value as 0 for valueX because it is still only pointing to the same place as 'a'
可以看出,这个转换只是指针地址的赋值。在reinterpret_cast的本质(http://blog.csdn.net/coding_hello/archive/2008/03/24/2211466.aspx)一文中,只有在cout的时候,参数被不同地进行的解释,才出现的不同的结果。
reinterpret_cast常用的场景如下:
1)普通指针转换,T*—>U*—>T*,保证T*经过一些列转换值不变
比如将不同类型的指针存在一个容器里,vector可以存int*,char*,string*等各种指针,只要有别的方式确定某个void*当初的类型是T*,标准保证reinterpret_cast(v[i])可以得到当初的值。
2)自己做memory allocator,可以将T*转换为U*,这个时候可能要注意字节对其的问题。
语法:T static_cast (expression);
该运算符把expression转换成type-id类型,但没有运行时类型检查来保证转换的安全性。
static_cast是最经常用到的转换操作符,它最经常的一个应用就是将隐式转换变成显示转换,以消除编译器可能产生的warning,同reinterpret_cast不同,采用static_cast转换的两个类型之间一般有某种相关性。
static_cast主要应用场景如下:
1)用于类层次结构中基类和派生类之间指针或引用的转换。这个转换中,将派生类转成基类是安全的,将基类转成派生类时由于没有进行动态类型检查,所以是不安全的。
2)用于基本数据之间的转换。如把int转成char,int转成num等。
3)把空指针转换成目标类型的空指针。
4)把任何类型的表达式转换成void类型。
除了空指针之外,static_cast并不经常用来做指针转换,因为它的效率不高。
int in=99; double dn=static_cast<double> (in);//用于转换基本类型和具有继承关系的类新之间转换 class Base{}; class derv:public Base{}; derv dd; Base bbbb=static_cast(dd);//具有继承关系的类型之间转换 Base *pb1=new Base; derv *pder=static_cast(pb1);//基类转继承类 derv* pder1=new derv; Base* pbase1=static_cast(pder1);//继承类指针转父类指针
语法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
dynamic_cast的转换是在运行时进行的,它的一个好处是会在运行是做类型检查,如果对象的类型不是期望的类型,它会在指针转换的时候返回NULL,并在引用转换的时候抛出一个std::bad_cast异常。
dynamic_cast一般只在继承类对象的指针之间或引用之间进行类型转换。如果没有继承关系,则被转化的类具有虚函数对象的指针进行转换。
struct A { virtual void f() { } }; struct B : public A { }; struct C { }; void f () { A a; B b; A* ap = &b; B* b1 = dynamic_cast (&a); // NULL, because 'a' is not a 'B' B* b2 = dynamic_cast (ap); // 'b' C* c = dynamic_cast (ap); // NULL. A& ar = dynamic_cast (*ap); // Ok. B& br = dynamic_cast (*ap); // Ok. C& cr = dynamic_cast (*ap); // std::bad_cast }
const_cast< type-id > (exdivssion)
这个运算符可以用来去除一个对象的const或volatile属性。type-id必须是一个指针或者引用。
class B{ public: int m_iNum; } void foo(){ const B b1; b1.m_iNum = 100; //comile error B b2 = const_cast((b1); b2. m_iNum = 200; //fine }