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++》,我做的只是按照我自己读书的习惯写成的记录。如果你有帮助,那就太好了。