C++類型轉換(Type Casting)

轉載自:http://blog.csdn.net/richerg85/article/details/7550909


所謂類型轉換即爲轉換一個給定的類型到另一個類型,我們已經知道一些類型轉換方式:

隱式類型轉換

       隱式類型轉換不需要請求任何操作。當一個值拷貝給另一個兼容的類型的時候會自動執行,例如:

  1. short a = 200;  
  2. int b;  
  3. b = a;  

       在此例中,a的值從short類型到int類型提升,並且不需要任何類型轉換操作。這個轉換是我們都知道的標準轉換。標準轉換影響基本的數據類型,並且只能轉換從數值類型(short->int,int->float,double->int等)到或者從bool,和一些指針轉換。一些轉換可能會丟失精度,這樣的話編譯器會提示警告,在顯示轉換中不會出現警告。

       隱式類型轉換也包括構造函數或者操作符轉換,例如:

  1. classA {};  
  2. class B {public: B (A a) {} };  
  3. A a;  
  4. B b=a;  

在此實例中,在類A和類B對象之間發生隱式轉換,因爲B帶一個參數爲類A對象。因此隱式類型轉換從A到B是允許的。

顯示類型轉換

       C++是一個強類型的語言,許多轉換尤其是那些指定值的不同解釋,需要顯示轉換。我們已經知道兩個顯示類型轉換的標記:函數和c樣式的類型轉換:

  1. shorta = 200;  
  2. intb;  
  3. b=  (int)a;   //C樣式類型標記  
  4. b =  int (a);   //函數類型標記  

對於大多數需求,對於函數類型函數的顯示類型操作已經足夠。但是,如果這些操作無差別的應用到類和指向類的指針上,可能導致在語法檢查中是正確的,但是運行時出現錯誤。例如,下面的代在碼語法結構上正確:

  1. #include <iostream>  
  2. using namespacestd;  
  3. class CDummy{  
  4.      floati,j;  
  5. };  
  6. class CAddition{  
  7.      intx,y;  
  8. public:  
  9.      CAddition(inta, int b){ x = a;y = b;}  
  10.      intResult(){returnx+y;}  
  11. };  
  12. int _tmain(intargc,_TCHAR*argv[])  
  13. {  
  14.      CDummyd;  
  15.      CAddition*padd;  
  16.      padd= (CAddition *)&d;  
  17.      cout<<padd->Result()<<endl;  
  18.      return0;  
  19. }  

這個程序聲明一個CAddition指針,然後通過顯示類型轉換分配給此指針另一個不相容類型對象的引用:

padd = (CAddition*)&d;

     傳統的顯示類型轉換允許轉換任何指針到任何其他類型的指針,並且它們指向的類型是完全獨立地。隨後的調用result成員函數會產生或者運行時錯誤或者不期望的值。

    爲了控制在類之間的轉換類型,c++有四個指定的轉換操作:dynamic_cast、static_cast、const_cast和reinterpret_cast。它們的格式遵從新的類型封裝,這個封裝在尖括號和隨後緊跟的在圓括號中的需要轉換的表達式之間轉換。

  1. dynamic_cast<new_type>(expression)  
  2. reinterpret_cast<new_type>(expression)  
  3. static_cast<new_type>(expression)  
  4. const_cast<new_type>(expression)  

傳統的類型轉換和這些表達式等效表達爲:

  1. (new_type)expression  
  2.  new_type(expression)  

但是每一個都有自己特定的特徵:

dynamic_cast

dynamic_cast僅用於對象的指針和引用,它確保類型轉換記過是一個完全合法的請求類對象。

因此,dynamic_cast在轉換一個類到一個其基類的時候,總是成功:

  1. class CBase{ };  
  2. class CDerived:public CBase{ };  
  3. CBase b; CBase* pb;  
  4. CDerived d; CDerived* pd;  
  5. pb = dynamic_cast<CBase*>(&d);    // ok:derived-to-base  
  6. pd = dynamic_cast<CDerived*>(&b); // wrong:base-to-derived  

在此程序片段中的第二個轉換會產生一個編譯錯誤,因爲在dynamic_cast中基類到繼承類轉換是不允許的,除非基類是多態的。

當一個類是多態的,dynamic_cast在運行時執行一個指定的檢查,確保表達式產生一個合法的請求類的完成對象:

    

  1. #include <iostream>  
  2. #include <exception>  
  3. class CBase{virtual voiddummy(){}};  
  4. class CDerived:public CBase{int a;};  
  5. int main()  
  6. {  
  7.      try  
  8.      {  
  9.          CBase *pba =new CDerived;  
  10.          CBase *pbb =new CBase;  
  11.          CDerived *pd;  
  12.          pd = dynamic_cast<CDerived *>(pba);  
  13.          if (pd == 0)  
  14.          {  
  15.               cout<< "NULLpointer on first type-cast" << endl;  
  16.          }  
  17.          pd = dynamic_cast<CDerived *>(pbb);  
  18.          if (pd == 0)  
  19.          {  
  20.               cout<<"NULLpointer on second type-cast" <<endl;  
  21.          }  
  22.      }  
  23.      catch (exception*e)  
  24.      {  
  25.          cout<< "Exception:"<< e->what()<<endl;  
  26.      }  
  27.      return 0;  
  28. }  

執行結果:NULL pointer on second type-cast

兼容提示:dynamic_cast要求實時類型信息(RTTI)保持對動態類型的跟蹤。一些編譯器支持這個特徵作爲可選項並且默認爲不支持的。在用dynamic_cast進行實時類型檢查時,這個需要修改成支持模式才能保證正常工作。

上面的代碼執行兩個動態類型轉換從CBase*到CDerived*,但是僅第一個轉換是成功的,這個需要注意它們各自的初始化不同:

CBase *pba=newCDerived;

         CBase *pbb =newCBase;

    雖然這兩個都是CBase*指針,pba指向類型CDerived對象,而pbb指向類型CBase對象。然後,當它們各自用dynamic_cas t執行類型轉換,pba指向完整的類CDerived對象,而pbb指向類CBase對象,並且pbb是一個不完整的類CDerived對象。

    當dynamic_cast不能轉換一個指針,由於一個請求類的不完整對象時,正如在上例中的第二個轉換,返回空指針指示轉換失敗。如果dynamic_cast用於轉換一個引用類型並且轉換是不可能時,一個類型爲bad_cast異常拋出。

    dynamic_cast也可以轉換空指針甚至在不相關的類指針,並且也可轉換任何類型的指針到空指針(void *)。

static_cast

    static_cast可以在相關的類之間執行轉換,不僅僅從派生類到基類,還可以從基類到派生類。這確保如果合適的對象被轉換,至少類是相關的。在運行期間沒有安全檢查,假設轉換的對象之間是一個完整對象類型(兩個類型是同種類型)。因此,這需要編程人員確保轉換時安全的,另一方面,前面的類似dynamic_cast的類型安全檢查是沒有的(avoided)。

  1. class CBase{};  
  2. class CDerived:public CBase{};  
  3. CBase * a= new CBase;  
  4. CDerived * b=static_cast<CDerived*>(a);  

    這個片段可能是合法的,雖然b可能指向一個不匹配的類對象,可能導致運行時錯誤如果解除參照。

    static_cast也可以用於執行任何其它的非指針轉換,這些轉換可以印刷轉換執行,例如基本的類型標準轉換:

  1. double d=3.14159265;  
  2.  int i =static_cast<int>(d);  

    或者任何在有顯示的構造函數或者操作符函數的類之間轉換,此種在上面“隱式轉換“中描述過。

reinterpret_cast

    reinterpret_cast轉換任何指針類型到任何其他的指針類型,甚至是不相關的類。操作結果是一個簡單的從一個指針到另一個指針的二進制值拷貝。允許所有的指針轉換:即不檢查指針內容也不檢查自身的指針類型。

    也可以轉換指針到(從)整數類型。代表一個指針的指針值的格式是平臺指定的。僅有的保證是一個指針轉換到一個整數類型需要足夠包括轉換的值,並且需要轉換回到合法的指針。

    在c++中由reninterpret_cast執行而不是由static_cast執行的轉換是低級別的操作,在系統指定的產生的代碼中的執行結果是不方便的,例如:

  1.     class A{};  
  2. class B{};  
  3. A * a= new A;  
  4. B * b= reinterpret_cast<B*>(a)  

    這是合法的c++代碼,雖然沒有什麼意義。你需要謹慎使用reinterpret_cast。

const_cast

    轉換類型操作對象常量,或者設定或者移除(常量)。例如,爲了傳遞一個常量參數到一個希望得到非常量參數的函數:

  1.  // const_cast  
  2. #include <iostream>  
  3. using namespacestd;  
  4. void print(char * str)  
  5. {  
  6.      cout << str<< endl;  
  7. }  
  8. int main() {  
  9.      const char * c = "sampletext";  
  10.      print ( const_cast<char *> (c) );  
  11.      return 0;  
  12. }  

運行結果:sampletext

typeid

typeid允許覈對表達式的類型:

typeid(expression)

這個操作符返回一個type_info類型的常對象引用,type_info類型是一個定義在標準頭文件<typeinfo>中。這個返回值可以和另一個值用==和!=比較或者獲得一個代表數據類型的非終止的字符序列或者通過它的name()成員函數獲得的類名。

  1. // typeid  
  2. #include <iostream>  
  3. #include <typeinfo>  
  4. using namespacestd;  
  5. int main() {  
  6.      int * a,b;  
  7.      a=0; b=0;  
  8.      if (typeid(a) !=typeid(b))  
  9.      {  
  10.          cout << "aand b are of different types:\n";  
  11.          cout << "ais: " << typeid(a).name()<<'\n';  
  12.          cout << "bis: " << typeid(b).name()<<'\n';  
  13.      }  
  14.      return 0;  
  15. }  

     執行結果:a and b are of different types:

a is: int *

b is: int

     當typeid應用到類,typeid用RTTI保持動態對象的類型的跟蹤。當typeid應用到一個類型是多態類的表達式,結果是大多數繼承完整對象類類型:

  1. // typeid, polymorphic class  
  2. #include <iostream>  
  3. #include <typeinfo>  
  4. #include <exception>  
  5. using namespacestd;  
  6. class CBase{ virtual voidf(){} };  
  7. class CDerived: public CBase{};  
  8. int main() {  
  9.      try {  
  10.          CBase* a = new CBase;  
  11.          CBase* b = new CDerived;  
  12.          cout << "ais: " << typeid(a).name()<<'\n';  
  13.          cout << "bis: " << typeid(b).name()<<'\n';  
  14.          cout << "*ais: " << typeid(*a).name()<<'\n';  
  15.          cout << "*bis: " << typeid(*b).name()<<'\n';  
  16.      }catch (exception&e) {cout<< "Exception: " <<e.what()<< endl; }  
  17.      return 0;  
  18. }  

    執行結果:a is: class CBase*

b is: class CBase *

*a is: class CBase

*b is: class CDerived

英文原文地址:http://www.cplusplus.com/doc/tutorial/typecasting/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章