相比于C++风格类型的转换,C风格的类型转换太过粗暴,允许你在任何类型之间进行转换。
1.例如把一个指向 const 对象的指针转换成指向非 const 对象的 指针(即一个仅仅去除 const 的类型转换),把一个指向基类的指针转换成指向子类的指针(即完全改变对象类型)。传统的C风格的类型转换不对上述两种转换进行区分。
2.C风格的类型转换在程序语句中难以识别。在语法上,类型转换由圆括号和标识符组成,而这些可以用在C++中的任何地方。人工阅读很可能忽略了类型转换的语句。
C++通过引进四个新的类型转换操作符克服了 C 风格类型转换的缺点,这四个操作符是, static_cast
, const_cast
, dynamic_cast
, 和 reinterpret_cast
static_cast
假设你想把一个 int 转换成 double,以便让包含 int 类型变量的表达式产生出浮点数值的结果。如果用 C 风格的类型转换,你能这样写:
int firstNumber, secondNumber;
double result = ((double)firstNumber)/secondNumber;
如果用上述新的类型转换方法,你应该这样写:
double result = static_cast<double>(firstNumber)/secondNumber;
const_cast
用于类型转换掉表达式的 const 或 volatileness 属性。通过使用 const_cast,你向人们和编译器强调你通 过类型转换想做的只是改变一些东西的 constness 或者 volatileness 属性。这个含义被编译器所约束。如果你试图使用const_cast来完成修改constness 或者volatileness 属性之外的事情,你的类型转换将被拒绝。
比如:
class Widget { ... };
class SpecialWidget: public Widget { ... };
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非 const 对象。
const SpecialWidget& csw = sw; // csw 是 sw 的一个引用
// 它是一个 const 对象
update(&csw); // 错误!不能传递一个 const SpecialWidget* 变量 给一个处理 SpecialWidget*类型变量的函数
update(const_cast<SpecialWidget*>(&csw)); // 正确,csw 的 const 被显示地转换掉( // csw 和 sw 两个变量值在 update //函数中能被更新)
update((SpecialWidget*)&csw); // 同上,但用了一个更难识别 //的 C 风格的类型转换 Widget *pw = new SpecialWidget;
update(pw); // 错误!pw 的类型是 Widget*,但是 update 函数处理的是 SpecialWidget*类型
update(const_cast<SpecialWidget*>(pw)); // 错误!const_cast 仅能被用在影响constness or volatileness 的地方上,不能用在向继承子类进行类型转换。
dynamic_cast
用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast
把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)
比如:
Widget *pw;
...
update(dynamic_cast<SpecialWidget*>(pw));
// 正确,传递给 update 函数一个指针
// 是指向变量类型为 SpecialWidget 的 pw 的指针
// 如果 pw 确实指向一个对象,
// 否则传递过去的将使空指针。
void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast<SpecialWidget&>(*pw));
//正确。 传递给 updateViaRef 函数
// SpecialWidget pw 指针,如果 pw
// 确实指向了某个对象
// 否则将抛出异常
dynamic_casts
在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上
比如:
这是因为int和double之间没有继承关系。
reinterpret_cast
reinterpret_casts
的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:
typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数的指针,该函数没有参数返回值类型为 void
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳10个FuncPtrs 指针的数组
假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
int doSomething();
你不能不经过类型转换而直接去做,因为 doSomething 函数对于 funcPtrArray 数组来说有一个错误的类型。在 FuncPtrArray数组里的函数返回值是void类型,而 doSomething函数返回值是 int 类型。
reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
但是要注意:
转换函数指针的代码是不可移植的,在一些情况下这样的转换会产生不正确的结果,所以你应该避免转换函数指针类型。