云枫的菜园子

带着一个流浪的心,慢慢沉淀。 https://github.com/CloudFeng

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Effective C++ 阅读笔记_条款27 尽量少做转型动作

1.转型的三种形式,可以分为两大类。

(1)旧式转型(old-style casts)
  (1.1) (T) expresstion
  (1.2) T (expression)
 (2) 新式转型(c++-style casts)
  (2.1)const_cast<T> (expression)
  (2.2)dynamic_cast<T> (expression)
  (2.3)reinterpret_cast<T> (expression)
  (2.4)static_cast<T> (expression)

2.新式转型的详细介绍
   (2.1)const_cast<T> (expression)
     通常被用来将对象的常量性转除。它可是唯一有此技能的操作符哟,厉害吧。

 (2.2)dynamic_cast<T> (expression)
     主要用来执行“安全向下转型”,也就是用来决定某对象是否归属继承体系中的某个类型。
  唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。
     dynamic_cast 的许多实现版本执行速度相当慢,特别是深度继承或多重继承。

  为啥要使用dynamics_cast操作符,又如何避免使用它呢。
    使用dynamics_cast是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你手上只有一个“指向base”的pointer或regerence,

     你只能靠他们来处理对象。

    解决方案有两种:
  (1)使用容器并在其中存储直接指向derived class 对象的指针
  比如:

 1         class Window {
 2                 // ...
 3             };
 4             class SpecialWindow: public Window {
 5             public:
 6                 void blink();
 7                 //...
 8             };
 9 
10             typedef std::vector<std::tr1::shared_ptr<Window>> VPW;
11             VPW winPtrs;
12             //...
13             for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) {
14                 if (SpecialWindow *psw = dynamic_cast<SpecialWindow *> (iter->get())) {
15                     psw=>blink;
16                 }
17             }
18            //--->修改如下:
19            typedef std::vector<std::tr1::shared_ptr<SpecialWindow>> VPSW;
20             VPSW winPtrs;
21             //...
22             for (VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) {
23                 (*iter)->blink();
24             }     

 

      缺点:无法在同一个容器内存存储指针“指向所有可能地各种window派生类”。

 (2)通过base class接口处理“所有可能地各种Window派生类”,就是在base class中提供virtual函数,比如:  

 1          class Window {
 2                 // ...
 3                 virtual void blink();
 4             };
 5             class SpecialWindow: public Window {
 6             public:
 7                 virtual void blink();
 8                 //...
 9             };
10 
11             typedef std::vector<std::tr1::shared_ptr<Window>> VPW;
12             VPW winPtrs;
13             //...
14             for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) {
15                 (*iter)->blink();
16             }

        另外注意:千万不要出现“连串”的dynamic_casts,这样的代码大而慢而且基础不稳定,维护性和扩展性都很差。

(2.3)reinterpret_cast<T> (expression)
       意图执行低级转型,实际动作与编译器有关。也表示它不可移植。

 (2.4)static_cast<T> (expression)
      强迫隐式转换,特别是反向转换,但是无法const-->non-const,这是const_cast的技能。

 1     class Window {
 2         public:
 3             // ....
 4             virtual void onResize() 
 5             {
 6                 //...
 7             }
 8         };
 9 
10         class SpecialWindow: public Window {
11         public:
12             virtual void onResize() {
13                 static_cast<Window>(*this).onResize();
14                 /*
15                     注意:当前对象进入一种“伤残”状态:其base class 成分的更改没有落实,
16                     而derived class成分的更改倒是落实了
17                 */
18                 // ....
19             }
20         };
21 
22         // --- 修正
23         class SpecialWindow: public Window {
24         public:
25             virtual void onResize() {
26                 Window::onResize();
27             }
28         };

3.记住,任何一个类型转换(不论是通过转型操作而进行的显示转换,或通过编译器完成的隐式转换)往往真的令编译器编译出运行期间执行的码。

总结:

   如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。

   如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内。

   宁可使用C++-style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌。

其实看完之后,感觉有点不用转型了,后来回头看看标题:尽量少做转型动作。

声明:本文所有的文字都是来源于《Effective C++》,我做的只是按照我自己读书的习惯写成的记录。如果你有帮助,那就太好了。

posted on 2015-03-21 18:34  CloudFeng  阅读(898)  评论(2编辑  收藏  举报