C++強制類型轉換總結

C/C++是有類型語言,在表達式計算,表達式賦值和函數調用時都會發生各種類型轉換。很多場合下,爲了使上述類型轉換合法、有效且滿足特定需求,我們需要對錶達式執行顯式的類型轉換。在這樣的場合,如何選擇合適的轉換函數,是我們不得不面對的一個問題。本篇我們總結整理了cpp文檔,對cpp中四種cast接口:const_caststatic_castdynamic_castreinterpret_cast進行了簡要的總結。

const_cast

const_cast<new_type>(exp: type):在有不同const,volatile限定符的類型間轉換。

  • 功能:移除表達式的常量性和易變性。 例如調用某個形參爲const T &的函數時,希望以(non-const)T類型作爲參數。

  • 分類:

    • new_type和type爲指向同一類型的指針,但是擁有不同的const,volatile限定符。
    • 空指針值可轉換成new_type的空指針值。
  • 常用例子:

    void Double(int & a) {
    	cout << a << endl;
    	a =  a * 2;
    	cout << a << endl;
    }
    
    int main(void) {
    	const int tmp = 1;
    	Double(const_cast<int &>(tmp));
    }
    

static_cast

static_cast<new_type>(exp: type): 用隱式轉換和用戶定義轉換的組合在類型間轉換。

  • 功能:顯式、靜態地執行隱式類型轉換和用戶定義的轉換,不進行運行時檢查。

  • 分類:

    • 隱式類型轉換和大部分隱式轉換的逆轉換。
    • 初始化轉換: 存在初始化函數 new_type(type)。
    • 子類到非虛父類的靜態轉換。
    • 左值到右值,數組到指針,函數到指針。
    • 棄值表達式:new_type爲void。
    • void * 到任何類型。
  • 常用例子:

    int n = static_cast<int>(3.14);  //float 轉 int
    std::vector<int> v = static_cast<std::vector<int>>(10); // 初始化轉換
    
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v); //左值到右值
    
    static_cast<void>(v2.size()); //棄值表達式
    
    void* voidp = &n;
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp); // void*轉其他
    

dynamic_cast

dynamic_cast<new_type >(exp:type):沿繼承層級向上、向下及側向,安全地轉換到其他類的指針和引用。

  • 功能:在進行繼承層級上的轉換時,執行運行時檢查,保證轉型的安全性。

  • 分類:

    • 向下轉型: type是new_type的公有基類,且從原對象中只能推導出一個new_type對象。
    • 側向轉型: type和new_type相互無繼承關係,但type和new_type都是原對象的公有基類。
    • 向上轉型: new_type是type的公有基類,可直接使用隱式類型轉換或者static_cast。
    • badcast: 均不滿足,指向new_type的空指針。
  • 常用例子:

    //           |--A--|
    // 虛基類 V-->|     |-->D 繼承關係表明:D同時繼承了A、B
    //           |--B--|
    A& a = d; // 向上轉型,可以用 dynamic_cast,但不必須
    D& new_d = dynamic_cast<D&>(a); // 向下轉型
    B& new_b = dynamic_cast<B&>(a); // 側向轉型
    

reinterpret_cast

reinterpret_cast<new_type>(exp: type):通過重新解釋底層位模式從而在類型間轉換。

  • 功能:基本在原地實現任何類型的互轉(去除cv限定符除外),但不保證使用安全性。

  • 分類:

    • 指針和整型互轉:注意該整型需要保證size足夠容納指針數值。這同時包括:任何空指針(賦值爲nullptr的指針)和空指針常量(nullptr),可轉換成任何整型類型。
    • 函數指針互轉:函數指針之間的互轉,不同類的成員函數指針的互轉。
    • 其他:type類型的左值表達式可轉換成new_type &(注意無tmp變量生成,不發生拷貝);type * 可轉 const/volatile new_type *; 任何具有空指針可轉換爲其他類型指針。
  • 常用例子:

    // 指針到整數並轉回
    int i = 7;
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i);
    int* p1 = reinterpret_cast<int*>(v1);
    
    // 到另一函數指針並轉回:已知f爲 int f() { return 0;}
    void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); 未定義行爲
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1); // fp2(); 安全
    
    // int 轉 unsigned int &
    reinterpret_cast<unsigned int&>(i) = 42;
    

四種Cast的對比

通過上面的總結,我們大致可以明白四種cast接口的區別可由其名稱前綴區分。

  • const_cast可以將const、volatile類型的cv限定符去掉。
  • dynamic_cast會做運行時動態類型檢查,它主要用於多態類型的繼承層級互轉。
  • static_cast使用範圍較廣,安全性較高。但當支持多態類的繼承關係轉換時,不會做運行時檢查,存在一定的安全性問題。注意用static_cast做轉換時,生成的新對象是原對象的拷貝版本,只是執行了類型轉換。
  • reinterpret_cast在編譯器層面解構了原類型,以新類型的觀點去解釋原類型的各個字節,故基本可以做任何類型轉換,例如不同類型函數指針互轉,指針與整型互轉等等。與static_cast不同但與const_cast類似的是,這種轉換不會對原值的執行拷貝,故若能正確轉回原類型,便能恢復變成原對象。很明顯,reinterpret_cast安全性問題較大
參考資料

[1]. cppreference. https://en.cppreference.com/w/ 2020,2,22

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