C++中四種強制類型轉換區別詳解

 C++即支持C風格的類型轉換,又有本身風格的類型轉換。C風格的轉換格式很簡單,可是有很多缺點的:程序員

  1.轉換太過隨意,能夠在任意類型之間轉換。你能夠把一個指向const對象的指針轉換成指向非const對象的指針,把一個指向基類對象的指針轉換成一個派生類對象的指針,這些轉換之間的差距是很是巨大的,可是傳統的C語言風格的類型轉換沒有區分這些。express

  2.C風格的轉換沒有統一的關鍵字和標示符。對於大型系統,作代碼排查時容易遺漏和忽略。ubuntu

  C++風格完美的解決了上面兩個問題。1.對類型轉換作了細分,提供了四種不一樣類型轉換,以支持不一樣需求的轉換;2.類型轉換有了統一的標示符,利於代碼排查和檢視。下面分別來介紹這四種轉換:static_cast、dynamic_cast、const_cast和reinterpre_cast.數組

1、static_cast轉換安全

  1.基本用法:static_cast<type-id> expression函數

  2.使用場景:設計

  a、用於類層次結構中基類和派生類之間指針或引用的轉換指針

  上行轉換(派生類---->基類)是安全的;對象

  下行轉換(基類---->派生類)因爲沒有動態類型檢查,因此是不安全的。內存

  b、用於基本數據類型之間的轉換,如把int轉換爲char,這種帶來安全性問題由程序員來保證

  c、把空指針轉換成目標類型的空指針

  d、把任何類型的表達式轉爲void類型

  3.使用特色

  a、主要執行非多態的轉換操做,用於代替C中一般的轉換操做

  b、隱式轉換都建議使用static_cast進行標明和替換

2、dynamic_cast轉換

  1.基本用法:dynamic_cast<type-id> expression

  2.使用場景:只有在派生類之間轉換時才使用dynamic_cast,type-id必須是類指針,類引用或者void*。

  3.使用特色:

  a、基類必需要有虛函數,由於dynamic_cast是運行時類型檢查,須要運行時類型信息,而這個信息是存儲在類的虛函數表中,只有一個類定義了虛函數,纔會有虛函數表(若是一個類沒有虛函數,那麼通常意義上,這個類的設計者也不想它成爲一個基類)。

  b、對於下行轉換,dynamic_cast是安全的(當類型不一致時,轉換過來的是空指針),而static_cast是不安全的(當類型不一致時,轉換過來的是錯誤意義的指針,可能形成踩內存,非法訪問等各類問題)

  c、dynamic_cast還能夠進行交叉轉換

3、const_cast轉換

  1.基本用法:const_cast<type-id>expression

  2.使用場景:

  a、常量指針轉換爲很是量指針,而且仍然指向原來的對象

  b、常量引用被轉換爲很是量引用,而且仍然指向原來的對象

  3.使用特色:

  a、cosnt_cast是四種類型轉換符中惟一能夠對常量進行操做的轉換符

  b、去除常量性是一個危險的動做,儘可能避免使用。一個特定的場景是:類經過const提供重載時,通常都是很是量函數調用const_cast<const T>將參數轉換爲常量,而後調用常量函數,而後獲得結果再調用const_cast <T>去除常量性。

4、reinterpret_cast轉換

  1.基本用法:reinterpret_cast<type-id>expression

  2.使用場景:不到萬不得已,不用使用這個轉換符,高危操做

  3.使用特色:  

  a、reinterpret_cast是從底層對數據進行從新解釋,依賴具體的平臺,可移植性差

  b、reinterpret_cast能夠將整型轉換爲指針,也能夠把指針轉換爲數組

  c、reinterpret_cast能夠在指針和引用裏進行肆無忌憚的轉換

5、各類轉換之間的比較

  1.static_cast和dynamic_cast

複製代碼

class Base
{
    public:
        Base(int c = 2):_c(c){}
    public:
        int _c;
};
class Derived:public Base
{
    public:
        int _d;
        int _e;
};
int main(void)
{
    int tempA = 2;
    int tempB = 3;
    Base base;
    
    /*1.無編譯告警,可是危險操做,譬如說調用drvPtrA->_d會形成不可預知的後果*/
    Derived *drvPtrA = static_cast<Derived *>(&base);
    
    drvPtrA->_d = 4;
    drvPtrA->_e = 5;
    /*2.輸出:tempA爲5,tempB爲4,踩內存了(機器信息:32位ubuntu,編譯器clang++)*/
    cout<<tempA<<endl;
    cout<<tempB<<endl;
    
    /*3.Base中沒有虛函數,沒法查看運行時信息,編譯不過*/
    Derived *drvPtrB = dynamic_cast<Derived *>(base);

    return 0;
}

複製代碼

  在基類派生類互相轉換時,雖然static_cast是在編譯期完成,效率更高,可是不安全,上例中就示範了一個踩內存的例子。相比之下由於dynamic_cast能夠查看運行時信息,上例若是Base含有虛函數,那麼drvPtrB就是一個空指針(這可比踩內存什麼的好多了),不能操做Derived中_d的數據從而保證安全性,因此應該優先使用dynamic_cast。

  2.static_cast和reinterpret_cast

複製代碼

class BaseA
{
    public:
        BaseA(int c = 2):_c(c){}
        int _c;
};
class BaseB
{
    public:
        BaseB(int d = 3):_d(d){}
        int _d;
};
int main(void)
{
    BaseA baseA;
    /*1.編譯不過*/
    BaseB *baseB = static_cast<BaseB *>(&baseA);
    /*2.無任何編譯告警,編譯經過,正常運行*/
    BaseB *baseC = reinterpret_cast<BaseB *>(&baseA);
    cout<<baseC->_d<<endl;

    return 0;
}

複製代碼

  static_cast雖然也不是一種絕對安全的轉換,可是它在轉換時,仍是會進行必要的檢測(諸如指針越界計算,類型檢查)。reinterpret_cast徹底是肆無忌憚,直接從二進制開始從新映射解釋,是極度不安全的,再次提醒,不到萬不得已,不要使用。

相關文章
相關標籤/搜索