[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++新型的转换,不用旧式转型

原文地址:https://www.cnblogs.com/hustcser/p/4211119.html