在编写C++程序时,有时会遇到对象类型需要转换的问题。在C中,通常是这种格式:(type) expr,即
1 double dval=110.0; 2 int ival=(int)dval;
这时dval就会被转换成整型变量,由 double 向 int 进行强制转换,而这种情况在C++也是适用的,不过对于其他更复杂的转换情形,C++给出了更好的强制显示转换形式。
C++有四种常见的强制类型转换操作符:static_cast,const_cast,dynamic_cast 和 reinterpret_cast,它们的表示形式为:cast-name<type>(expression),如:const_cast<char *>(cchar);
1、static_cast
static_cast 基本适用于将大类型变量向小类型的转换或者是小类型向大类型的转换,但是它也有功能上限制,例如,你不能用 static_cast 象用 C 风格的类型转换一样把 struct 转换成 int 类型或者把 double 类型转换成指针类型。
例如,假设你想把一个 int 转换成 double,以便让包含 int 类型变量的表达式产生出浮点数值的结果。如果用 C 风格的类型转换,你能这样写:
1 int firstNumber, secondNumber; 2 ... 3 double result = ((double)firstNumber)/secondNumber;
如果用上述新的类型转换方法,你应该这样写:
1 double result = static_cast<double(firstNumber)/secondNumber;
这样的类型转换不论是对人工还是对程序都很容易识别。平时从一个较大类型到一个较小类型的赋值时,会导致编译器产生一个警告以提醒我们潜在的精度损失。当我们提供显示强制转换时,警告消息被关闭,强制转换会告诉编译器和程序的读者:我们不关心潜在的精度损失。
2、const_cast
显而易见,const_cast 用于类型转换掉表达式的 const 或 volatileness 属性。通过使用 const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的 constness 或者 volatileness 属性。这个含义被编译器所约束。如果你试图使用 const_cast 来完成修改 constness 或者 volatileness 属性之外的事情,你的类型转换将被拒绝。下面是一些例子:
1 class Widget { ... }; 2 class SpecialWidget: public Widget { ... }; 3 void update(SpecialWidget *psw); 4 SpecialWidget sw; // sw 是一个非 const 对象。 5 const SpecialWidget& csw = sw; // csw 是 sw 的一个引用 6 // 它是一个 const 对象 7 update(&csw); // 错误!不能传递一个 const SpecialWidget* 变量 8 // 给一个处理 SpecialWidget*类型变量的函数 9 update(const_cast<SpecialWidget*>(&csw)); 10 // 正确,csw 的 const 被显示地转换掉( 11 // csw 和 sw 两个变量值在 update 12 // 函数中能被更新) 13 update((SpecialWidget*)&csw); 14 // 同上,但用了一个更难识别 15 // 的 C 风格的类型转换 16 Widget *pw = new SpecialWidget; 17 update(pw); // 错误!pw 的类型是 Widget*,但是 18 // update 函数处理的是 SpecialWidget*类型 19 update(const_cast<SpecialWidget*>(pw)); 20 // 错误!const_cast 仅能被用在影响 21 // constness or volatileness 的地方上。, 22 // 不能用在向继承子类进行类型转换。
基本上,const_cast 最常见的用途是转换掉对象的 const 属性。
3、dynamic_cast
它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用 dynamic_cast 把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时):
1 Widget *pw; 2 ... 3 update(dynamic_cast<SpecialWidget*>(pw)); 4 // 正确,传递给 update 函数一个指针 5 // 是指向变量类型为 SpecialWidget 的 pw 的指针 6 // 如果 pw 确实指向一个对象, 7 // 否则传递过去的将使空指针。 8 void updateViaRef(SpecialWidget& rsw); 9 updateViaRef(dynamic_cast<SpecialWidget&>(*pw)); 10 // 正确。 传递给 updateViaRef 函数 11 // SpecialWidget pw 指针,如果 pw 12 // 确实指向了某个对象 13 // 否则将抛出异常
dynamic_casts 在帮助你浏览继承层次上是有限制的,它不能被用于缺乏虚函数的类型上也不能用它来转换掉 constness:
1 int firstNumber, secondNumber; 2 ... 3 double result = dynamic_cast<double>(firstNumber)/secondNumber; 4 // 错误!没有继承关系 5 const SpecialWidget sw; 6 ... 7 update(dynamic_cast<SpecialWidget*>(&sw)); 8 // 错误! dynamic_cast 不能转换 9 // 掉 const。
4、reinterpret_cast
reinterpret_casts 的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:
1 typedef void (*F uncPtr)(); // FuncPtr is 一个指向函数 2 // 的指针,该函数没有参数 3 // 返回值类型为 void 4 FuncPtr funcPtrArr ay[10]; // funcPtrArray 是一个能容纳 5 // 10 个 FuncPtrs 指针的数组
让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray 数组: int doSomething(); 你不能不经过类型转换而直接去做,因为 doSomething 函数对于 funcPtrArray 数组来说有一个错误的类型。在 FuncPtrArray 数组里的函数返回值是 void 类型,而 doSomething函数返回值是 int 类型。
1 funcPtrArray[0] = &doSomething; // 错误!类型不匹配 2 //reinterpret_cast 可以让你迫使编译器以你的方法去看待它们: 3 funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); // this compiles
转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果,所以你应该避免转换函数指针类型。
如果你使用的编译器缺乏对新的类型转换方式的支持,你可以用传统的类型转换方法代替 static_cast, const_cast, 以及 reinterpret_cast。也可以用下面的宏替换来模拟新的类型转换语法:
#define static_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))
你可以象这样使用使用:
double result = static_cast(double, firstNumber)/secondNumber;
update(const_cast(SpecialWidget*, &sw));
funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);
这些模拟不会象真实的操作符一样安全,但是当你的编译器可以支持新的的类型转换时,它们可以简化你把代码升级的过程。
没有一个容易的方法来模拟 dynamic_cast 的操作,但是很多函数库提供了函数,安全地在派生类与基类之间进行类型转换。如果你没有这些函数而你有必须进行这样的类型转换,你也可以回到 C 风格的类型转换方法上,但是这样的话你将不能获知类型转换是否失败。当然,你也可以定义一个宏来模拟 dynamic_cast 的功能,就象模拟其它的类型转换一样:
#define dynamic_cast(TYPE,EXPR) (TYPE)(EXPR)
请记住,这个模拟并不能完全实现 dynamic_cast 的功能,它没有办法知道转换是否失败。