C++風格的各種類型轉換

儘量使用C++風格的類型轉換  
  仔細想想地位卑賤的類型轉換功能(cast),其在程序設計中的地位就象goto語句一樣令人鄙視。但是它還不是無法令人忍受,因爲當在某些緊要的關頭,類型轉換還是必需的,這時它是一個必需品。  
  不過C風格的類型轉換並不代表所有的類型轉換功能。  
  一來它們過於粗魯,能允許你在任何類型之間進行轉換。不過如果要進行更精確的類型轉換,這會是一個優點。在這些類型轉換中存在着巨大的不同,例如把一個指向const對象的指針(pointer-to-const-object)轉換成指向非const對象的指針(pointer-to-non-const-object)(即一個僅僅去除const的類型轉換),把一個指向基類的指針轉換成指向子類的指針(即完全改變對象類型)。傳統的C風格的類型轉換不對上述兩種轉換進行區分。(這一點也不令人驚訝,因爲C風格的類型轉換是爲C語言設計的,而不是爲C++語言設計的)。  
  二來C風格的類型轉換在程序語句中難以識別。在語法上,類型轉換由圓括號和標識符組成,而這些可以用在C++中的任何地方。這使得回答象這樣一個最基本的有關類型轉換的問題變得很困難:“在這個程序中是否使用了類型轉換?”。這是因爲人工閱讀很可能忽略了類型轉換的語句,而利用象grep的工具程序也不能從語句構成上區分出它們來。  
  C++通過引進四個新的類型轉換操作符克服了C風格類型轉換的缺點,這四個操作符是,   static_cast,   const_cast,   dynamic_cast,   和reinterpret_cast。在大多數情況下,對於這些操作符你只需要知道原來你習慣於這樣寫,  
  (type)   expression  
  而現在你總應該這樣寫:  
  static_cast<type>(expression)  
  例如,假設你想把一個int轉換成double,以便讓包含int類型變量的表達式產生出浮點數值的結果。如果用C風格的類型轉換,你能這樣寫:  
  int   firstNumber,   secondNumber;  
  ...  
  double   result   =   ((double)firstNumber)/secondNumber;  
  如果用上述新的類型轉換方法,你應該這樣寫:  
  double   result   =   static_cast<double>(firstNumber)/secondNumber;  
  這樣的類型轉換不論是對人工還是對程序都很容易識別。  
  static_cast在功能上基本上與C風格的類型轉換一樣強大,含義也一樣。它也有功能上限制。例如,你不能用static_cast象用C風格的類型轉換一樣把struct轉換成int類型或者把double類型轉換成指針類型,另外,static_cast不能從表達式中去除const屬性,因爲另一個新的類型轉換操作符const_cast有這樣的功能。  
  其它新的C++類型轉換操作符被用在需要更多限制的地方。const_cast用於類型轉換掉表達式的const或volatileness屬性。通過使用const_cast,你向人們和編譯器強調你通過類型轉換想做的只是改變一些東西的constness或者   volatileness屬性。這個含義被編譯器所約束。如果你試圖使用const_cast來完成修改constness   或者volatileness屬性之外的事情,你的類型轉換將被拒絕。下面是一些例子:  
  class   Widget   {   ...   };  
  class   SpecialWidget:   public   Widget   {   ...   };  
  void   update(SpecialWidget   *psw);  
  SpecialWidget   sw;                                 //   sw   是一個非const   對象。  
  const   SpecialWidget&   csw   =   sw;       //   csw   是sw的一個引用  
                                                                  //   它是一個const   對象    
  update(&csw);     //   錯誤!不能傳遞一個const   SpecialWidget*   變量  
                                //   給一個處理SpecialWidget*類型變量的函數    
  update(const_cast<SpecialWidget*>(&csw));  
  //   正確,csw的const被顯示地轉換掉(  
  //   csw和sw兩個變量值在update  
  //函數中能被更新)    
  update((SpecialWidget*)&csw);  
                                                    //   同上,但用了一個更難識別  
                                                    //的C風格的類型轉換  
  Widget   *pw   =   new   SpecialWidget;    
  update(pw);                   //   錯誤!pw的類型是Widget*,但是  
                                          //   update函數處理的是SpecialWidget*類型    
  update(const_cast<SpecialWidget*>(pw));  
                                          //   錯誤!const_cast僅能被用在影響  
                                          //   constness   or   volatileness的地方上。,  
                                          //   不能用在向繼承子類進行類型轉換。  
  到目前爲止,const_cast最普通的用途就是轉換掉對象的const屬性。  
  第二種特殊的類型轉換符是dynamic_cast,它被用於安全地沿着類的繼承關係向下進行類型轉換。這就是說,你能用dynamic_cast把指向基類的指針或引用轉換成指向其派生類或其兄弟類的指針或引用,而且你能知道轉換是否成功。失敗的轉換將返回空指針(當對指針進行類型轉換時)或者拋出異常(當對引用進行類型轉換時):  
  Widget   *pw;  
  ...  
  update(dynamic_cast<SpecialWidget*>(pw));  
  //   正確,傳遞給update函數一個指針  
  //   是指向變量類型爲SpecialWidget的pw的指針  
  //   如果pw確實指向一個對象,  
  //   否則傳遞過去的將使空指針。  
  void   updateViaRef(SpecialWidget&   rsw);  
  updateViaRef(dynamic_cast<SpecialWidget&>(*pw));  
                                                    //正確。   傳遞給updateViaRef函數  
                                                    //   SpecialWidget   pw   指針,如果pw    
                                                    //   確實指向了某個對象  
                                                    //   否則將拋出異常  
  dynamic_casts在幫助你瀏覽繼承層次上是有限制的。它不能被用於缺乏虛函數的類型上(參見條款M24),也不能用它來轉換掉constness:  
  int   firstNumber,   secondNumber;  
  ...  
  double   result   =   dynamic_cast<double>(firstNumber)/secondNumber;  
                                                    //   錯誤!沒有繼承關係  
  const   SpecialWidget   sw;  
  ...  
  update(dynamic_cast<SpecialWidget*>(&sw));  
                                                    //   錯誤!   dynamic_cast不能轉換  
                                                    //   掉const。  
  如你想在沒有繼承關係的類型中進行轉換,你可能想到static_cast。如果是爲了去除const,你總得用const_cast。  
  這四個類型轉換符中的最後一個是reinterpret_cast。使用這個操作符的類型轉換,其的轉換結果幾乎都是執行期定義(implementation-defined)。因此,使用reinterpret_casts的代碼很難移植。  
  reinterpret_casts的最普通的用途就是在函數指針類型之間進行轉換。例如,假設你有一個函數指針數組:  
  typedef   void   (*FuncPtr)();             //   FuncPtr   is   一個指向函數  
                                                                  //   的指針,該函數沒有參數  
  //   返回值類型爲void  
  FuncPtr   funcPtrArray[10];               //   funcPtrArray   是一個能容納  
                                                                  //   10個FuncPtrs指針的數組  
  讓我們假設你希望(因爲某些莫名其妙的原因)把一個指向下面函數的指針存入funcPtrArray數組:  
  int   doSomething();  
  你不能不經過類型轉換而直接去做,因爲doSomething函數對於funcPtrArray數組來說有一個錯誤的類型。在FuncPtrArray數組裏的函數返回值是void類型,而doSomething函數返回值是int類型。  
  funcPtrArray[0]   =   &doSomething;           //   錯誤!類型不匹配    
  reinterpret_cast可以讓你迫使編譯器以你的方法去看待它們:  
  funcPtrArray[0]   =                                       //   this   compiles  
      reinterpret_cast<FuncPtr>(&doSomething);  
  轉換函數指針的代碼是不可移植的(C++不保證所有的函數指針都被用一樣的方法表示),在一些情況下這樣的轉換會產生不正確的結果(參見條款M31),所以你應該避免轉換函數指針類型,除非你處於着背水一戰和尖刀架喉的危急時刻。一把鋒利的刀。一把非常鋒利的刀。  
  如果你使用的編譯器缺乏對新的類型轉換方式的支持,你可以用傳統的類型轉換方法代替static_cast,   const_cast,   以及reinterpret_cast。也可以用下面的宏替換來模擬新的類型轉換語法:  
  #define   static_cast(TYPE,EXPR)               ((TYPE)(EXPR))  
  #define   const_cast(TYPE,EXPR)                 ((TYPE)(EXPR))  
  #define   reinterpret_cast(TYPE,EXPR)     ((TYPE)(EXPR))  
  你可以象這樣使用使用:  
  double   result   =   static_cast(double,   firstNumber)/secondNumber;  
  update(const_cast(SpecialWidget*,   &sw));  
  funcPtrArray[0]   =   reinterpret_cast(FuncPtr,   &doSomething);  
  這些模擬不會象真實的操作符一樣安全,但是當你的編譯器可以支持新的的類型轉換時,它們可以簡化你把代碼升級的過程。  
  沒有一個容易的方法來模擬dynamic_cast的操作,但是很多函數庫提供了函數,安全地在派生類與基類之間進行類型轉換。如果你沒有這些函數而你有必須進行這樣的類型轉換,你也可以回到C風格的類型轉換方法上,但是這樣的話你將不能獲知類型轉換是否失敗。當然,你也可以定義一個宏來模擬dynamic_cast的功能,就象模擬其它的類型轉換一樣:  
  #define   dynamic_cast(TYPE,EXPR)           (TYPE)(EXPR)  
  請記住,這個模擬並不能完全實現dynamic_cast的功能,它沒有辦法知道轉換是否失敗。  
  我知道,是的,我知道,新的類型轉換操作符不是很美觀而且用鍵盤鍵入也很麻煩。如果你發現它們看上去實在令人討厭,C風格的類型轉換還可以繼續使用並且合法。然而,正是因爲新的類型轉換符缺乏美感才能使它彌補了在含義精確性和可辨認性上的缺點。並且,使用新類型轉換符的程序更容易被解析(不論是對人工還是對於工具程序),它們允許編譯器檢測出原來不能發現的錯誤。這些都是放棄C風格類型轉換方法的強有力的理由。還有第三個理由:也許讓類型轉換符不美觀和鍵入麻煩是一件好事。  
   
   
  載自《More   Effecitve   C++》
發佈了25 篇原創文章 · 獲贊 0 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章