“尽量少做转型动作”
—— 《Effective C++》条款27
1. 引言
本文源自在工作中的一点疑惑,先看一下下面的代码。这是一个使用了ACE架构的系统中的一段代码,它使用ACE_Message_Block将一个任务对象指针添加在执行器executorDataProcessing中。
即便你不了解ACE的使用方式也没有关系,我们并不关注ACE,它只是一个引子。那么,reinterpret_cast的含义?为什么要使用reinterpret_cast?何时使用reinterpret_cast?类型转换还有哪几种?
2. 四种C++新式转型
(1)reinterpret_cast<type_id>(expression)
type_id可以是指针,引用,基本类型。
reinterpret_cast运算符用来将一种类型的指针转变为另一种类型的指针,也用在将整型量转为指针,或将指针转为整型量。
需要注意的是,基本类型之间的转换不能使用reinterpret_cast进行转换,但是可以通过引用来进行,但是正确性不能保证。
(2)static_cast<type_id>(expression)
static_cast不同于是dynamic_cast,没有运行时类型检查用于基本类型之间的转换,其转换的安全性要由程序开发者来承担。用于类层次结构中基类和子类之间指针或引用的转换,进行上行转换(把子类的指针或引用转换成基类的指针或引用)是安全的;进行下行转换(把基类指针或引用转换成子类的指针或引用)时,由于没有运行时类型检查,所以是不安全的。
需要注意:不能在无关的指针之间使用static_cast进行强制转换
(3)dynamic_cast<type_id>(expression)
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有运行时类型检查的功能,比static_cast更安全。
(4)const_cast<type_id>(expression)
该运算符用来修改类型的const、volatile、__unaligned 属性。除了const、volatile、__unaligned 修饰之外,type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
3. 类型转换使用导引(后续可能还会添加)
(1)若要消除对象(包括基本数据类型)的常量性,请使用const_cast
(2)若要完成从基类对象向继承类对象的“安全向下转型”,请使用dynamic_cast
(3)若要完成从继承类对象向基类对象的“向上转型”,请使用static_cast
(4)若要完成两种类型指针间的转型,或者整型量与某类型指针间的转型,请使用reinterpret_cast
(5)若要完成基本数据类型间的转型,请使用static_cast
4. 总结
为什么说上面的四种转型是“新式转型”呢?实际上在C++中还有两种旧式风格转型,包括C风格的转型:
(T)expression // 将expression转型为T
以及函数风格的转型:
T(expression) // 将expression转型为T
相对于旧式转型,四种新式转型应该优先选择,原因有二:
(1)它们很容易在代码中被辨识出来,继而得以简化“找出类型系统在哪个地点被破坏”的过程。
(2)各转型动作的目标愈窄化,编译器愈可能诊断出错误的运行。
5. 参考
题外话
如果可以,尽量避免转型,特别是在注重效率的代码中避免使用dynamic_casts。如果有个设计需要转型动作,试着开发无需转型的替代设计。