EffectiveC++-条款27:尽量少做转型动作

一. 内容

  1. C++规则的设计目标之一是,保证类型错误绝不可能发生。理论上如果你的代码很干净的通过编译,就表示它并不企图在任何对象身上执行任何不安全的,无意义的,荒谬的操作。你应该始终坚持这一点。

  2. 不幸的是,转型破坏了类型系统,这可能导致任何种类的错误,有些容易识别,有些则相当隐晦。

  3. 我们先回顾转型语法。C风格的转型:(T)expression。函数风格的转型:T(expression)。两种形式并无差别,纯粹只是小括号摆放位置不一样而已,这是旧式转型。C++ 还提供四种新型转型,常常被称为 new-style 或者C++ -style casts。

    const_cast<T>(expression)
    dynamic_cast<T>(expression)
    reinterpret_cast<T>(expression)
    static_cast<T>(expression)
    

    各有各的用法。

    • const_cast 通常用来将常对象的常量性转除。它也是唯一一个可以移除常量性的转型操作符
    • dynamic_cast 主要用来执行 base-derived 体系的安全向下转型。它是唯一无法由旧时语法执行的动作,也是唯一可能花费重大运行成本的转型动作。
    • reinterpret_cast 用以执行低级转型,实际动作及结果可能取决于编译器,这也表示它不可被移植。比如 将一块被解释成 int 的 0x00636261 重新解释为 char 为 abc,除非编写底层代码,一般很少见。
    • static_cast 用来强制隐式转换,例如将 non-const 转换成 const,或将 int 转换成 double。
  4. 旧式语法虽然合法,但新式语法较受欢迎。原因:

    • 它们很容易在代码中被辨识出来,可以简化找出类型系统在何处被破坏的过程。
    • 各个转型动作的目标明确,编译器发现转型错误的可能性更高。举个例子,当你想将 对象的 const 移除,唯一可以使用的转型操作符只能是 const_cast。
  5. 理智使用新式转型。转型不只是告诉编译器把某种类型视作另一种类型,往往还产出了实际的运行码。比如将一个 derived class 对象的指针赋值给 base class 对象的指针,在赋值时会进行隐式转换,这时会有一个偏移量加在 derived class指针上,用来获得正确的 base* 值。

  6. 关于 dynamic_cast 转型,它的许多实现版本速度相当慢,例如一个基于class名称的字符串比较,对于一个四重单继承的体系,可能会用到四次 strcmp的调用。深度继承或者多重继承的成本更高。这样实现不是没有原因,它们必须支持动态连接。之所以需要 dynamic_cast 转型,通常是因为你手上只有一个指向 base 的指针或者引用,但你想使用derived class的操作函数,需要进行转换。有两种方法可以避免这个问题:

    • 使用容器存储直接指向 derived class 对象的指针,通常是智能指针,参见条款13。

    • 在base class 提供 virtual 函数做那些想对 derived class 做的事情。
      绝对要避免以下代码:

      class Window {
      public:
          virtual ~Window();
      };
      
      class WindowOne : public Window { };
      
      class WindowTwo : public Window { };
      
      class WindowThree : public Window { };
      
      inline void Try(Window* Window) {
          if (WindowOne* One = dynamic_cast<WindowOne*>(Window)) {
              //...
          }
          else if (WindowTwo* Two = dynamic_cast<WindowTwo*>(Window)) {
              //...
          }
          else if (WindowThree* Three = dynamic_cast<WindowThree*>(Window)) {
              //...
          }
      }
      

      这样产生出来的代码又大又慢,而且基础不稳。因为每次 Window class 继承体系一有改变,所有这类代码都需要重新编写。这样的代码应该总是由某些基于 virtual 函数调用的东西取而代之。

      另外注意,dynamic_cast 只能作用于具备多态性质的base class,意味着 base class 至少具有一个 virtual 函数

  7. 当然,优良的C++代码很少使用转型,但要说完全摆脱它们又太不切实际,所以我们应该尽量隔离转型行为,通常是将它们隐藏在某个private函数中。

二. 总结

  1. 如果可以,尽量避免转型,特别是注重效率的代码中避免 dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
  2. 如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放入他们自己的代码内。
  3. 宁可使用C++ - syle新式转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值