旧的转型的第一个劣势是它几乎允许你将任何类型转换为任何其他类型,这是很糟糕的。如果每次转型都能更精确地指明意图,则更好。举个例子,将一个pointer-to-const-object 转型为pointer-to-non-const-object (也就是说只改变对象的常量性)和将一个pointer-to-base-class-object 转型为pointer-to-derived-class-object(也就是完全改变了一个对象的类型),其间有很大的差异。传统的C转型动作对此并无区分。旧的转型的第二个问题是他们难以辨识。旧式转型是一对小括号加上一个对象名称组成,而()在C++程序中随处可见,我们都无法回答最基本的问题:这个程序中有使用任何的转型动作吗?
为了解决C旧式转型的缺点,C++引入了4个新的转型操作符(cast operators):static_cast、const_cast、dynamic_cast、reinterpret_cast。
- static_cast
以前这样写:(type) expression
现在这样写:static_cast <type> expression
举个例子:假如你想将一个int 转型为一个double,以强迫一个整数表达式导出一个浮点数,采用新型C++的写法为:
Int firstNumber,secondNumber;
Double result=static_cast<double> firstNumber/secondNumber
这种形式很容易就标识出来,对于人类和对于工具程序而言都是如此。static_cast基本上与C旧式转型相同的威力和限制。
- const_cast
const_cast用来改变表达式中的常量性(constness)或变易性(volatileness),使用const_cast,便是强调,通过这个转型操作符,你唯一打算改变的是某物的常量性或变易性,这个意愿由编译器来执行。下面是个例子:
Class Widget{......};
Class SpecialWidget:public Widget{.......};
Void update(SpecialWidget *psw);
SpecialWidget sw;//sw 是个non-const 对象
const SpecialWidget& csw=sw;//csw却是一个代表sw的reference,并视之为一个const 对象
update(&csw);//错误!不能将const SpecialWidget* 传给需要SpecialWidget *psw 的函数
update(const_cast<SpecialWidget *>(&csw));//可以的 &csw 的常量性被去除了。因此,csw (亦即 sw)在此函数中是可以被更改的。
update((SpecialWidget*)&csw);//可以的 &csw 的常量性被去除了。因此,csw (亦即 sw)在此函数中是可以被更改的。//但是这种使用的是难以辨识的C旧式转型语法 。
Widget *pw=new SpecialWidget*;
update(pw);//错误! pw的类型是Widget*,但是update()需要的是 SpecialWidget*
update(const_cast<SpecialWidget*>(pw));//错误!const_cast只能用来影响常量性或易变性,无法进行继承体系的向下转型(cast down)动作。
显然,const_cast最常见的用途就是将某个对象的常量性去掉。
- Dynamic_cast
用来执行继承体系中“安全的向下转型或跨系转型动作”。也即是说你可以利用dynamic_cast,将“指向base class object的pointer或reference”转型为“指向derived (或sibling base )class objects的pointer 或reference”,并得知转型是否成功1.如果失败,会以一个null指针(当转型对象是指针)或一个exception(当转型对象是reference),下面是代码示例:
Widget *pw;
......
update(dynamic_cast<SpecialWidget*>(pw));//很好,传给update()一个指针指向pw所指的SpecialWidget---如果pw真的指向了这样的东西;
//否则传过去的是一个null指针
//声明一个新的函数
void updateViaRef(SpecialWidget& rsw);
void updateViaRef(dynamic_cast<SpecialWidget&>(*pw));//很好,传给updateViaRef()的是pw所指的SpecialWidget--如果pw真的指向了这样的东西;
//否则,抛出一个exception
dynamic_cast只能用来协助你巡航于继承体系之中。它无法应用在缺乏虚函数的类型身上,也不能改变类型的常量性。
- Reinterpret_cast
这个操作符的转换结果几乎总是与编译平台息息相关,所以reinterpre_cast不具备移植性。
reinterpre_cast的最常用用途是转换“函数指针”类型。假设有一个数组,存储的都是函数指针,有特定的类型:
typedef void (*FuncPtr)()//FuncPtr 是个指针,指向某个函数。该函数无需任何自变量,返回值为void
FuncPtr funcPtrArray[10];// funcPtrArray是个数组,内有10个 FuncPtrs
假设由于某种原因,你希望将以下函数的一个指针放进 funcPtrArray中
int doSomething();
如果没有转型,不可能办到这一点,因为doSomething的类型与funcPtrArray所能接受的不同。funcPtrArray内各函数指针所指函数的返回值是void,而doSomething的返回类型是int。
funcPtrArray[0]=&doSomething;// 错误的,类型不符
//使用reinterpret_cast,可以让编译器了解你的意图
funcPtrArray[0]=reinterpret_cast<FuncPtr>(&doSomething); //这样就通过了编译
函数指针的转型动作,不具备移植性,所以应该尽量避免使用函数指针转型,除非你走投无路了。
使用新式转型的三个好处:
- 使用了新式的转型比较容易辨识(对人,对编译器)都是如此。
- 编译器也得以诊断转型错误(旧式的转型侦测不到)
- 让转型动作既丑陋又难以使用(言外之意就是最好不要做转型这个事情,太low啦!)