条款2:最好使用C++(www.cppentry.com) 转型操作符(2)
dynamic_cast 只能用来协助你巡航于继承体系之中。它无法应用在缺乏虚函数(请看条款24)的类型身上,也不能改变类型的常量性(constness):
- int firstNumber, secondNumber;
- ...
- double result =
- dynamic_cast<double>(firstNumber)/secondNumber;
- // 错误!未涉及继承机制。
- const SpecialWidget sw;
- ...
- update(dynamic_cast<SpecialWidget*>(&sw));
- // 错误!dynamic_cast 不能改变常量性。
如果你想为一个不涉及继承机制的类型执行转型动作,可使用 static_cast;要改变常量性(constness),则必须使用 const_cast。
最后一个转型操作符是 reinterpret_cast。这个操作符的转换结果几乎总是与编译平台息息相关。所以 reinterpret_casts 不具移植性。
reinterpret_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);
函数指针的转型动作,并不具移植性(C++(www.cppentry.com) 不保证所有的函数指针都能以此方式重新呈现),某些情况下这样的转型可能会导致不正确的结果(见条款31),所以你应该尽量避免将函数指针转型,除非你已走投无路,像是被逼到墙角,而且有一把刀子抵住你的喉咙。一把锐利的刀子,非常锐利的刀子。
如果你的编译器尚未支持这些新式转型动作,你可以使用传统转型方式来取代 static_cast,const_cast 和 reinterpret_cast。甚至可以利用宏(macros)来仿真这些新语法。
- #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,所以它无法告诉你转型是否成功。
我知道,我知道,这些新式转型操作符看起来又臭又长。如果你实在看它们不顺眼,值得安慰的是 C 旧式转型语法仍然可继续使用。然而这么一来也就丧失了新式转型操作符所提供的严谨意义与易辨识度。如果你在程序中使用新式转型法,比较容易被解析(不论是对人类还是对工具而言),编译器也因此得以诊断转型错误(那是旧式转型法侦测不到的)。这些都是促使我们舍弃 C 旧式转型语法的重要因素。至于可能的第三个因素是:让转型动作既丑陋又不易键入(typing),或许未尝不是件好事。