C语言风格的旧式转型动作有两种形式:
(T)expression //将expression转型为T
T(expression) //将expression转型为T
两种形式并无差别。
C++还提供四种新式转型:
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)
各有不同的目的:
1.const_cast通常被用来将对象的常量属性移除(cast away the constness)。它也是唯一有此能力的C++ style转型操作符。
2.dynamic_cast主要用来执行“安全向下转型”(safe downcasting),也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。
3.reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int转型为一个int。这一类转型在低阶代码以外很少见。
4.static_cast用来强迫隐士转换(implicit conversions),例如将non-const对象转为const对象,或将int转为double等等。它也可以用来执行上述多种转换的反向转换,例如将void*指针转为typed指针,将pointer-to-base转为pointer-to-derived。但它无法将const转为non-const——这个只有const——cast才办得到。
C语言风格的旧式转型任然合法,但是新式转型较受欢迎。原因:第一,它们很容易在代码中被辨识出来(不论是人工辨识或使用工具如grep),因而得以简化“找出类型系统中哪个地点被破坏”的过程。第二,各转型动作的目标愈窄化,编译器愈可能诊断出错误的运用。比如,如果打算将constness去掉,除非使用新式转型中的const_cast否则无法通过编译。
之所以需要dynamic_cast,通常是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你的手上却只有一个“指向base”的pointer或reference,你只能靠它们来处理对象。有两个一般性做法可以避免这个问题。
第一,使用容器并在其中存储直接指向derived class对象的指针(通常是智能指针),如此便消除了“通过base class接口处理对象”的需要。
另一种做法可让你通过base class接口处理“所有可能之各种Window派生类”,那就是在base class内提供virtual函数做你想对各个Window派生类做的事。
dynamic_cast的许多实现版本执行速度相当慢。深度继承或多重继承的成本更高!
优良的C++代码很少使用转型,但若说要完全摆脱它们又太过于不切实际。
要点:
1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着发展无需转型的替代设计。
2.如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码中。
3.宁可使用C++style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌。