首页 > 代码库 > [Effective C++ --027]尽量少做转型动作
[Effective C++ --027]尽量少做转型动作
引言
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)
第一节 C++各种转型的区别
?const_cast 一般用于强制消除对象的常量性。它是唯一能做到这一点的 C++ 风格的强制转型。
?dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”,也就是说,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。
?reinterpret_cast 是特意用于底层的强制转型,导致实现依赖(implementation-dependent)(就是说,不可移植)的结果,例如,将一个指针转型为一个整数。这样的强制转型在底层代码以外应该极为罕见。
?static_cast 可以被用于强制隐型转换(例如,non-const 对象转型为 const 对象,int 转型为 double,等等),它还可以用于很多这样的转换的反向转换(例如,void* 指针转型为有类型指针,基类指针转型为派生类指针),但是它不能将一个 const 对象转型为 non-const 对象(只有 const_cast 能做到),它最接近于C-style的转换。
区别:
dynamic_cast可用于继承体系中的向下转型,即将基类指针转换为派生类指针,比static_cast更严格更安全。dynamic_cast在执行效率上比static_cast要差一些,但static_cast在更宽上范围内可以完成映射,这种不加限制的映射伴随着不安全性.static_cast覆盖的变换类型除类层次的静态导航以外,还包括无映射变换,窄化变换(这种变换会导致对象切片,丢失信息),用VOID*的强制变换,隐式类型变换等。
reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它.我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的.(这句话是C++编程思想中的原话)
static_cast 和 reinterpret_cast 操作符修改了操作数类型. 它们不是互逆的; static_cast 在编译时使用类型信息执行转换, 在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的. 另一方面, reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。
总结:
1、static_cast,支持子类指针到父类指针的转换,并根据实际情况调整指针的值,反过来也支持,但会给出编译警告,它作用最类似C风格的“强制转换”,一般来说可认为它是安全的;
2、dynamic_cast,支持父类指针到子类指针的转换,并根据实际情况调整指针的值,和static_cast不同,反过来它就不支持了,会导致编译错误,这种转换是最安全的转换;
3、reinterpret_cast,支持任何转换,但仅仅是如它的名字所描述的那样“重解释”而已,不会对指针的值进行任何调整,用它完全可以做到“指鹿为马”,但很明显,它是最不安全的转换,使用它的时候,你得头脑清醒,知道自己在干什么;
4、const_cast,这个转换能剥离一个对象的const属性,也就是说允许你对常量进行修改。
第二节 转型的使用
假设我们有以下的代码
1 class Base {...};2 class Derived: public Base {...};3 Derived d;4 Base* pb = &d; // 隐喻的将Derived*转换为Base*
在上述代码中,我们建立了一个base class指针指向派生类对象,但有时候上述的两个指针的值并不相同。这种情况下会有偏移量在运行期间被施与Derived指针上,用以取得正确的Base*指针值。
这就表明,单一对象(比如派生类对象)可能拥有一个以上的地址(比如以Base*指向它时的地址和以Derived*指向它时的地址)。事实上,一旦发生多重继承,这事一直都在发生。
需要注意的是:上面的偏移量是随着编译器不同而不同的。
在调用基类的虚函数的时候,也很容易出现以下的问题:
需求:在基类和派生类都定义了虚函数的时候,如果我们想要在派生类中调用父类的函数。
1 class Base { 2 public: 3 virtual void onResize() {...}; 4 }; 5 6 class Device: public Base { 7 public: 8 virtual void onResize() { 9 static_cast<Base>(*this).onResize(); // 调用基类的onResize函数,实际是不可行的10 ...11 }12 };
在红色的代码中,*this转型为Base,调用Base::onResize。但是,它调用的并不是当前对象上的函数,而是稍早转型动作所建立的一个“*this对象的Base Class”的副本。
改法很简单,拿掉转型,直接调用即可:
1 class Base { 2 public: 3 virtual void onResize() {...}; 4 }; 5 6 class Device: public Base { 7 public: 8 virtual void onResize() { 9 Base::onResize(); // 调用基类的onResize函数10 ...11 }12 };
在这一节中,作者还讲述了两种避免转型的方法,其实原理都是一样的:直接调用,避免过多的转型!
◆总结
1.如果可以,尽量避免转型,特别是在注重效率的代码中避免使用dynamic_cast
2.如果必须要转型,请试着将其隐藏在某个函数背后
3.宁可使用C++新型的转换,不用旧式转型
[Effective C++ --027]尽量少做转型动作