在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
1、static_cast
语法形式:
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之间没有任何关系,那么会报编译错误。
2、dynamic_cast
语法形式:
dynamic_cast <type-id> ( expression)
把expression转换成type-id类型,type-id必须是一个类的指针、类的引用或者void*,如果type-id是一个指针,那么expression必须是一个指针,如果type-id是引用,那么expression必须是一个引用。
dynamic_cast主要用于类层次之间的上行转换和下行转换,还可以用于类之间的交叉转换(Cross)。
dynamic_cast必须用于有继承关系的类之间,如果两个类指针之间没有任何关系,则会报编译错误。
在VisualC++ 2005中,当type-id不是所转类型的指针的话,dynamic_cast不会再抛异常,它会返回0。
在类层次之间进行上行转换时,dynamic_cast和static_cast的效果是一样的,但在进行下行转换时,dynamic_cast具有类型检查功能,比static_cast更安全。(如果基类指针根本就不是指向子类,dynamic_cast返回NULL,而static_cast照样能转换成功,此时是不安全的)。
我们一起来看一下下面这段代码:
//dynamic_cast_1.cpp
// compilewith: /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
}
voidfunc(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还支持交叉转换:
classA
{
public:
intm_iNum;
virtual voidf(){}
};
classB:public A{};
classD:public A{};
voidfoo()
{
B *pb = newB;
pb->m_iNum = 100;
D *pd1 =static_cast<D *>(pb); //copileerror
D *pd2 =dynamic_cast<D *>(pb); //pd2 isNULL
deletepb;
}
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用dynamic_cast的转换则是允许的,结果是空指针。
3、const_cast
语法形式:
const_cast <type-id> ( expression)
该运算符用来修改类型的constg或volatile属性。除了const或volatile修饰之外,type-id和expression的类型是一样的。
这个用得最多的就是常量指针转换成非常量指针,常量引用转换成非常量引用,并且仍是指向原来对象或仍是原来对象的引用。
举个例子:
classB
{
public:
intm_iNum;
}
voidfoo()
{
const Bb1;
b1.m_iNum =100; //comile error
B b2 =const_cast<B>(b1);
b2. m_iNum =200; //fine
}
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。
4、reinterpret_cast
语法形式:
reinterpret_cast <type-id> (expression )
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以将char* 转成int*,类A的指针转成类B的指针,这样的转换是不安全的。reinperpre_cast不能去掉const,volatile或__unaligned属性。它可以将一个NULL指针转换成目标类型的NULL指针。
//expre_reinterpret_cast_Operator.cpp
// compilewith: /EHsc
#include<iostream>
// Returns ahash code based on an address
unsignedshort Hash( void *p ) {
unsigned intval = reinterpret_cast<unsigned int>(p );
return (unsigned short )( val ^ (val >>16));
}
usingnamespace std;
int main(){
inta[20];
for ( int i= 0; i < 20; i++ )
cout<< Hash( a + i )<< endl;
}