C++類型轉(zhuǎn)換總結(jié)

C++類型轉(zhuǎn)換總結(jié)

本章內(nèi)容:
1 前言
2 static_cast
3 dynamic_cast
4 const_cast
5 reinterpret_cast


1 前言

  • C++中類型轉(zhuǎn)換分為兩種:

    1. 隱式類型轉(zhuǎn)換
    2. 顯式類型轉(zhuǎn)換。
  • 對于隱式類型轉(zhuǎn)換,其實(shí)就是標(biāo)準(zhǔn)的轉(zhuǎn)換,在很多時(shí)候不經(jīng)意間就發(fā)生了,比如intfloat相加的時(shí)候,int類型就會被隱式轉(zhuǎn)換為float類型,然后再進(jìn)行加運(yùn)算。而今天的重點(diǎn)主要講解顯式類轉(zhuǎn)換,在C++中有四個(gè)類型轉(zhuǎn)換符:static_castdynamic_castconst_castreinterpret_cast,下面我們對這四個(gè)轉(zhuǎn)換符進(jìn)行總結(jié)。

2 static_cast

  • static_cast的轉(zhuǎn)換格式:static_cast<type-id> (expression)
  • expression轉(zhuǎn)換為type-id類型,主要用于非多態(tài)類型之間的轉(zhuǎn)換,不提供運(yùn)行時(shí)的檢查來確保轉(zhuǎn)換的安全性。主要在以下幾種場合中使用:
    1. 用于類層次結(jié)構(gòu)中,基類和子類之間指針和引用的轉(zhuǎn)換;
      當(dāng)進(jìn)行上行轉(zhuǎn)換,也就是把子類的指針或引用轉(zhuǎn)換成父類表示,這種轉(zhuǎn)換是安全的;
      當(dāng)進(jìn)行下行轉(zhuǎn)換,也就是把父類的指針或引用轉(zhuǎn)換成子類表示,這種轉(zhuǎn)換是不安全的,也需要程序員來保證;
    2. 用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char,把int轉(zhuǎn)換成enum等等,這種轉(zhuǎn)換的安全性需要程序員來保證;
    3. void指針轉(zhuǎn)換成目標(biāo)類型的指針,是及其不安全的;
      注:static_cast不能轉(zhuǎn)換expressionconstvolatile__unaligned屬性。

3 dynamic_cast

  • dynamic_cast的轉(zhuǎn)換格式:dynamic_cast <type-id> (expression)

  • expression轉(zhuǎn)換為type-id類型,type-id必須是類的指針、類的引用或者是void *;如果type-id是指針類型,那么expression也必須是一個(gè)指針;如果type-id是一個(gè)引用,那么expression也必須是一個(gè)引用。

  • dynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換,還可以用于類之間的交叉轉(zhuǎn)換。在類層次間進(jìn)行上行轉(zhuǎn)換時(shí),dynamic_caststatic_cast的效果是一樣的;在進(jìn)行下行轉(zhuǎn)換時(shí),dynamic_cast具有類型檢查的功能,比static_cast更安全。在多態(tài)類型之間的轉(zhuǎn)換主要使用dynamic_cast,因?yàn)轭愋吞峁┝诉\(yùn)行時(shí)信息。下面我將分別在以下的幾種場合下進(jìn)行dynamic_cast的使用總結(jié):

    1. 最簡單的上行轉(zhuǎn)換
  • 比如B繼承自A,B轉(zhuǎn)換為A,進(jìn)行上行轉(zhuǎn)換時(shí),是安全的,示例如下:
    #include <iostream>
    using namespace std;
    class A
    {
    // ......
    };
    class B : public A
    {
    // ......
    };
    int main()
    {
    B *pB = new B;
    A *pA = dynamic_cast<A *>(pB); // Safe and will succeed
    }

    1. 多重繼承之間的上行轉(zhuǎn)換
      C繼承自B,B繼承自A,這種多重繼承的關(guān)系;但是,關(guān)系很明確,使用dynamic_cast進(jìn)行轉(zhuǎn)換時(shí),也是很簡單的:
      class A
      {
      // ......
      };
      class B : public A
      {
      // ......
      };
      class C : public B
      {
      // ......
      };
      int main()
      {
      C *pC = new C;
      B *pB = dynamic_cast<B *>(pC); // OK
      A *pA = dynamic_cast<A *>(pC); // OK
      }
  • 而上述的轉(zhuǎn)換,static_castdynamic_cast具有同樣的效果。而這種上行轉(zhuǎn)換,也被稱為隱式轉(zhuǎn)換;比如我們在定義變量時(shí)經(jīng)常這么寫:B *pB = new C;這和上面是一個(gè)道理的,只是多加了一個(gè)dynamic_cast轉(zhuǎn)換符而已。

    1. 轉(zhuǎn)換成void *
  • 可以將類轉(zhuǎn)換成void *,例如:
    class A
    {
    public:
    virtual void f(){}
    // ......
    };
    class B
    {
    public:
    virtual void f(){}
    // ......
    };
    int main()
    {
    A *pA = new A;
    B *pB = new B;
    void *pV = dynamic_cast<void *>(pA); // pV points to an object of A
    pV = dynamic_cast<void *>(pB); // pV points to an object of B
    }

  • 但是,在類A和類B中必須包含虛函數(shù),為什么呢?因?yàn)轭愔写嬖谔摵瘮?shù),就說明它有想讓基類指針或引用指向派生類對象的情況,此時(shí)轉(zhuǎn)換才有意義;由于運(yùn)行時(shí)類型檢查需要運(yùn)行時(shí)類型信息,而這個(gè)信息存儲在類的虛函數(shù)表中,只有定義了虛函數(shù)的類才有虛函數(shù)表。

    1. 如果expressiontype-id的基類,使用dynamic_cast進(jìn)行轉(zhuǎn)換時(shí),在運(yùn)行時(shí)就會檢查expression是否真正的指向一個(gè)type-id類型的對象,如果是,則能進(jìn)行正確的轉(zhuǎn)換,獲得對應(yīng)的值;否則返回NULL,如果是引用,則在運(yùn)行時(shí)就會拋出異常;例如:
      class B
      {
      virtual void f(){};
      };
      class D : public B
      {
      virtual void f(){};
      };
      void main()
      {
      B* pb = new D; // unclear but ok
      B* pb2 = new B;
      D* pd = dynamic_cast<D>(pb); // ok: pb actually points to a D
      D
      pd2 = dynamic_cast<D*>(pb2); // pb2 points to a B not a D, now pd2 is NULL
      }
  • 這個(gè)就是下行轉(zhuǎn)換,從基類指針轉(zhuǎn)換到派生類指針。
    對于一些復(fù)雜的繼承關(guān)系來說,使用dynamic_cast進(jìn)行轉(zhuǎn)換是存在一些陷阱的;比如,有如下的一個(gè)結(jié)構(gòu):


    轉(zhuǎn)換結(jié)構(gòu)
  • D類型可以安全的轉(zhuǎn)換成B和C類型,但是D類型要是直接轉(zhuǎn)換成A類型呢?

      class A
      {
          virtual void Func() = 0;
      };
      class B : public A
      {
          void Func(){};
      };
      class C : public A
      {
          void Func(){};
      };
      class D : public B, public C
      {
          void Func(){}
       };
      int main()
      {
          D *pD = new D;
          A *pA = dynamic_cast<A *>(pD); // You will get a pA which is NULL
      }
    
  • 如果進(jìn)行上面的直接轉(zhuǎn),你將會得到一個(gè)NULLpA指針;這是因?yàn)椋珺和C都繼承了A,并且都實(shí)現(xiàn)了虛函數(shù)Func,導(dǎo)致在進(jìn)行轉(zhuǎn)換時(shí),無法進(jìn)行抉擇應(yīng)該向哪個(gè)A進(jìn)行轉(zhuǎn)換。正確的做法是:

      int main()
      {
          D *pD = new D;
          B *pB = dynamic_cast<B *>(pD);
          A *pA = dynamic_cast<A *>(pB);
      }
    
  • 這就是我在實(shí)現(xiàn)QueryInterface時(shí),得到IUnknown的指針時(shí),使用的是*ppv = static_cast<IX *>(this);而不是*ppv = static_cast<IUnknown *>(this);

  • 對于多重繼承的情況,從派生類往父類的父類進(jìn)行轉(zhuǎn)時(shí),需要特別注意;比如有下面這種情況:


    多重集成結(jié)構(gòu)
  • 現(xiàn)在,你擁有一個(gè)A類型的指針,它指向E實(shí)例,如何獲得B類型的指針,指向E實(shí)例呢?如果直接進(jìn)行轉(zhuǎn)的話,就會出現(xiàn)編譯器出現(xiàn)分歧,不知道是走E->C->B,還是走E->D->B。對于這種情況,我們就必須先將A類型的指針進(jìn)行下行轉(zhuǎn)換,獲得E類型的指針,然后,在指定一條正確的路線進(jìn)行上行轉(zhuǎn)換。

4 const_cast

  • const_cast的轉(zhuǎn)換格式:const_cast <type-id> (expression)

  • const_cast用來將類型的constvolatile__unaligned屬性移除。常量指針被轉(zhuǎn)換成非常量指針,并且仍然指向原來的對象;常量引用被轉(zhuǎn)換成非常量引用,并且仍然引用原來的對象。看以下的代碼例子:

      #include <iostream>
      using namespace std;
      class CA
      {
      public:
          CA():m_iA(10){}
          int m_iA;
      };
      int main()
      {
          const CA *pA = new CA;
          // pA->m_iA = 100; // Error
          CA *pB = const_cast<CA *>(pA);
          pB->m_iA = 100;
          // Now the pA and the pB points to the same object
          cout<<pA->m_iA<<endl;
          cout<<pB->m_iA<<endl;
          const CA &a = *pA;
          // a.m_iA = 200; // Error
          CA &b = const_cast<CA &>(a);
          b.m_iA = 200;
          // Now the a and the b reference to the same object
          cout<<b.m_iA<<endl;
          cout<<a.m_iA<<endl;
      }
    
  • 注:你不能直接對非指針和非引用的變量使用const_cast操作符去直接移除它的constvolatile__unaligned屬性。

5 reinterpret_cast

  • reinterpret_cast的轉(zhuǎn)換格式:reinterpret_cast <type-id> (expression)
  • 允許將任何指針類型轉(zhuǎn)換為其它的指針類型;聽起來很強(qiáng)大,但是也很不靠譜。它主要用于將一種數(shù)據(jù)類型從一種類型轉(zhuǎn)換為另一種類型。它可以將一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),也可以將一個(gè)整數(shù)轉(zhuǎn)換成一個(gè)指針,在實(shí)際開發(fā)中,先把一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),在把該整數(shù)轉(zhuǎn)換成原類型的指針,還可以得到原來的指針值;特別是開辟了系統(tǒng)全局的內(nèi)存空間,需要在多個(gè)應(yīng)用程序之間使用時(shí),需要彼此共享,傳遞這個(gè)內(nèi)存空間的指針時(shí),就可以將指針轉(zhuǎn)換成整數(shù)值,得到以后,再將整數(shù)值轉(zhuǎn)換成指針,進(jìn)行對應(yīng)的操作。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容