標籤:
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++》,我做的只是按照我自己讀書的習慣寫成的記錄。如果你有協助,那就太好了。
Effective C++ 閱讀筆記_條款27 盡量少做轉型動作