在C++中,经常会涉及到类型转换,虽说一般情况下不建议类型转换,但有时候还是避免不了。转换的时候,可能一般都直接使用C语言风格的转换(直接强制转换),但这样做可能很不安全,容易造成数据丢失(如int -> char),内存访问违规。
下面讲一讲C++的几个转换操作符,常见的有这么几个:static_cast, dynamic_cast, const_cast, reinterpret_cast。这里所讲的几乎都是参考MSDN,地址如下:
ms-help://MS.MSDNQTR.v90.chs/dv_vclang/html/16240348-26bc-4f77-8eab-57253f00ce52.htm
语法形式:
static_cast <type-id> ( expression )
该操作符把expression转换成type-id的类型,这里面没有类型检查,所以它的安全性不能得到保证。
1,static_cast可以用于转换基类指针到派生类指针,进行上行转换(把子类的指针或引用转换成基类指针)是安全,进行下行转换(基类指针或引用转成子类指针)是不安全的。
2,用于基本数据类型之间的转换,如把int转成char,int转成enum,这种转换的安全性只能由开发人员来保证,因为int转char可能会导致数据丢失,从而造成逻辑错误。
3,static_cast可以转换空指针到目标类型的空指针。
4,static_cast可以把任何类型转换成void类型,目标类型可以包含const,volatile,或__unaligned属性。
5,static_cast不能转换掉const,volatile,或__unaligned属性。
6,如果要用来转换类指针,如果类A与类B之间没有任何关系,那么会报编译错误。
语法形式:
dynamic_cast <type-id> ( expression )
把expression转换成type-id类型,type-id必须是一个类的指针、类的引用或者void*,如果type-id是一个指针,那么expression必须是一个指针,如果type-id是引用,那么expression必须是一个引用。
dynamic_cast主要用于类层次之间的上行转换和下行转换,还可以用于类之间的交叉转换(Cross)。
dynamic_cast必须用于有继承关系的类之间,如果两个类指针之间没有任何关系,则会报编译错误。
在Visual C++ 2005中,当type-id不是所转类型的指针的话,dynamic_cast不会再抛异常,它会返回0。
在类层次之间进行上行转换时,dynamic_cast和static_cast的效果是一样的,但在进行下行转换时,dynamic_cast具有类型检查功能,比static_cast更安全。(如果基类指针根本就不是指向子类,dynamic_cast返回NULL,而static_cast照样能转换成功,此时是不安全的)。
我们一起来看一下下面这段代码:
// dynamic_cast_1.cpp // compile with: /c class B { }; class C : public B { }; class D : public C { }; void f(D* pd) { C* pc = dynamic_cast<C*>(pd); // ok: C is a direct base class // pc points to C subobject of pd B* pb = dynamic_cast<B*>(pd); // ok: B is an indirect base class // pb points to B subobject of pd } void func(B *pb){ D *pd1 = static_cast<D *>(pb); D *pd2 = dynamic_cast<D *>(pb); }
在上面的代码中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的,但是,如果pb指向的是一个B类型的对象,那么static_cast也能转换成功,那么用pd1(D类型)去访问D类型的一些方法时,可能就会出异常,而对于dynicma_cast,它就会返回一个NULL,表示转换不成功,注意了,这个函数是有性能开销的,因为它要进行类型检查等操作。
另外,对于dynamic_cast,B类型需要有虚函数,否则编译要出错(这种情况必须是下行转换,即从上向下转换),因为dynamic_cast会进行运行时类型检查,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才会有虚函数表,没有定义虚数的类是没有虚函数表的,所以编译就会出错,而对于static_cast则没有限制,因为它不进行运行时类型检查。
dynamic_cast还支持交叉转换:
class A { public: int m_iNum; virtual void f(){} }; class B:public A{}; class D:public A{}; void foo() { B *pb = new B; pb->m_iNum = 100; D *pd1 = static_cast<D *>(pb); //copile error D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL delete pb; }
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
语法形式:
const_cast <type-id> ( expression )
该运算符用来修改类型的constg或volatile属性。除了const或volatile修饰之外,type-id和expression的类型是一样的。
这个用得最多的就是常量指针转换成非常量指针,常量引用转换成非常量引用,并且仍是指向原来对象或仍是原来对象的引用。
举个例子:
class B { public: int m_iNum; } void foo() { const B b1; b1.m_iNum = 100; //comile error B b2 = const_cast<B>(b1); b2. m_iNum = 200; //fine }
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。
语法形式:
reinterpret_cast <type-id> ( expression )
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以将char* 转成 int*,类A的指针转成类B的指针,这样的转换是不安全的。reinperpre_cast不能去掉const, volatile或__unaligned属性。它可以将一个NULL指针转换成目标类型的NULL指针。
// expre_reinterpret_cast_Operator.cpp // compile with: /EHsc #include <iostream> // Returns a hash code based on an address unsigned short Hash( void *p ) { unsigned int val = reinterpret_cast<unsigned int>( p ); return ( unsigned short )( val ^ (val >> 16)); } using namespace std; int main() { int a[20]; for ( int i = 0; i < 20; i++ ) cout << Hash( a + i ) << endl; }