zoukankan      html  css  js  c++  java
  • [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++新型的转换,不用旧式转型

  • 相关阅读:
    [CareerCup] 8.1 Implement Blackjack 实现21点纸牌
    [LeetCode] H-Index 求H指数
    [CareerCup] 7.7 The Number with Only Prime Factors 只有质数因子的数字
    [CareerCup] 7.6 The Line Passes the Most Number of Points 经过最多点的直线
    Ionic实战一:Ionic仿照微信项目
    ionic3+angular4+cordova 项目实例
    Android 给EditText添加下划线
    浅谈移动优先的跨终端Web 解决方案
    Android环信即时通讯集成坑爹 注册报错208解决
    Android自定义控件 -- 带边框的TextView
  • 原文地址:https://www.cnblogs.com/hustcser/p/4211119.html
Copyright © 2011-2022 走看看