C++中的四種類型轉換運算符static_cast、dynamic_cast、const_cast和reinterpret_cast的使用


1、上一遍講述了C語言的隱式類型轉換和顯示類型轉換,C語言之所以增加強制類型轉換,就是為了強調轉換的風險性,但這種強調風險的方式是比較粗放了,粒度比較大,它並沒有表明存在什么風險,風險程度如何。

2、為了使潛在風險更加細化,使問題追溯更加方便,使書寫格式更加規范,C++ 對類型轉換進行了分類,並新增了四個關鍵字來予以支持,它們分別是:

關鍵字 說明
static_cast 用於良性轉換,一般不會導致意外發生,風險很低。
const_cast 用於 const 與非 const、volatile 與非 volatile 之間的轉換。
reinterpret_cast 高度危險的轉換,這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,但是可以實現最靈活的 C++ 類型轉換。
dynamic_cast 借助 RTTI,用於類型安全的向下轉型(Downcasting)。

這四個關鍵字的語法格式都是一樣的,具體為:

xxx_cast<newType>(data)

newType 是要轉換成的新類型,data 是被轉換的數據。例如:

老式的C風格的 double 轉 int 的寫法為:

  double scores = 95.5;

  int n = (int)scores;

C++ 新風格的寫法為:

  double scores = 95.5;

  int n = static_cast<int>(scores);

3、static_cast:

  一、只能用於良性轉換,這樣的轉換風險較低,一般不會發生什么意外,例如:

  • 原有的自動類型轉換,例如 short 轉 int、int 轉 double、向上轉型等;
  • void 指針和具體類型指針之間的轉換,例如void *int *char *void *等;
  • 有轉換構造函數或者類型轉換函數的類與其它類型之間的轉換。

  二、需要注意的是,static_cast 不能用於無關類型之間的轉換,因為這些轉換都是有風險的,例如:

  • 兩個具體類型指針之間的轉換,例如int *double *Student *int *等。
  • int 和指針之間的轉換。將一個具體的地址賦值給指針變量是非常危險的,因為該地址上的內存可能沒有分配,也可能沒有讀寫權限,恰好是可用內存反而是小概率事件。

  三、static_cast 也不能用來去掉表達式的 const 修飾和 volatile 修飾。換句話說,不能將 const/volatile 類型轉換為非 const/volatile 類型。
  四、static_cast 是“靜態轉換”的意思,也就是在編譯期間轉換,轉換失敗的話會拋出一個編譯錯誤。

  下面的代碼演示了 static_cast 的正確用法和錯誤用法:

  class Complex

  {

  public:

    Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }

  public:

    operator double() const { return m_real; } //類型轉換函數

  private:

    double m_real;

    double m_imag;

  };

  

  int main()

  {

    //下面是正確的用法

    int m = 100;

    Complex c(12.5, 23.8);

    long n = static_cast<long>(m);           //寬轉換,沒有信息丟失

    char ch = static_cast<char>(m);          //窄轉換,可能會丟失信息

    int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) ); //將void指針轉換為具體類型指針

    void *p2 = static_cast<void*>(p1);         //將具體類型指針,轉換為void指針

    double real= static_cast<double>(c);         //調用類型轉換函數

    //下面的用法是錯誤的

    float *p3 = static_cast<float*>(p1); //不能在兩個具體類型的指針之間進行轉換

    p3 = static_cast<float*>(0X2DF9); //不能將整數轉換為指針類型

    return 0;

  }

4、const_cast:

  const_cast 比較好理解,它用來去掉表達式的 const 修飾或 volatile 修飾。換句話說,const_cast 就是用來將 const/volatile 類型轉換為非 const/volatile 類型。

  下面的代碼說明 const_cast 的用法:

  int main()

  {

    const int n = 100;

    int *p = const_cast<int*>(&n);

    *p = 234;

    cout<<"n = "<<n<<endl;     //n=100

    cout<<"*p = "<<*p<<endl;  //*p=234;

    return 0;

  }

5、reinterpret_cast:

  reinterpret 是“重新解釋”的意思,顧名思義,reinterpret_cast 這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,非常簡單粗暴,所以風險很高。
  reinterpret_cast 可以認為是 static_cast 的一種補充,一些 static_cast 不能完成的轉換,就可以用 reinterpret_cast 來完成,例如兩個具體類型指針之間的轉換、int 和指針之間的轉換(有些編譯器只允許   int 轉指針,不允許反過來)。
  下面的代碼代碼說明 reinterpret_cast 的使用:

  class A

  {

  public:

    A(int a = 0, int b = 0): m_a(a), m_b(b){}

  private:

 

    int m_a;

    int m_b;

  };

 

  int main()

  {

    char str[]="http://c.biancheng.net";  //將 char* 轉換為 float*

    float *p1 = reinterpret_cast<float*>(str);

    cout<<*p1<<endl;

    int *p = reinterpret_cast<int*>(100);//將 int 轉換為 int*

    p = reinterpret_cast<int*>(new A(25, 96));//將 A* 轉換為 int*

    cout<<*p<<endl;

    return 0;

  }

  運行結果:

  3.0262e+29
  25
  可以想象,用一個 float 指針來操作一個 char 數組是一件多么荒誕和危險的事情,這樣的轉換方式不到萬不得已的時候不要使用。將A*轉換為int*,使用指針直接訪問 private 成員刺穿了一個類的封裝性,更好的辦法是讓類提供 get/set 函數,間接地訪問成員變量。

6、dynamic_cast:

  dynamic_cast 用於在類的繼承層次之間進行類型轉換,它既允許向上轉型(Upcasting),也允許向下轉型(Downcasting)。向上轉型是無條件的,不會進行任何檢測,所以都能成功;向下轉型的前提必須是安全的,要借助 RTTI 進行檢測,所有只有一部分能成功。
  dynamic_cast 與 static_cast 是相對的,dynamic_cast 是“動態轉換”的意思,static_cast 是“靜態轉換”的意思。dynamic_cast 會在程序運行期間借助 RTTI 進行類型轉換,這就要求基類必須包含虛函數;static_cast 在編譯期間完成類型轉換,能夠更加及時地發現錯誤。
  dynamic_cast 的語法格式為:

  dynamic_cast <newType> (expression)

  newType 和 expression 必須同時是指針類型或者引用類型。換句話說,dynamic_cast 只能轉換指針類型和引用類型,其它類型(int、double、數組、類、結構體等)都不行。
  對於指針,如果轉換失敗將返回 NULL;對於引用,如果轉換失敗將拋出std::bad_cast異常。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM