釐清C++類型轉換

2021-02-19 C語言與C++編程

作者:LeeHappen

連結:https://www.jianshu.com/p/7d2bdf22dd28

C++是一門弱類型的語言,提供了許多複雜和靈巧類型轉換的方式。筆者之前寫的Python與Go都是強類型的語言,對這種弱類型的設計實在是接受無力啊 ( 生活所迫,工作還得寫C++啊)C++語言提供了四種類型轉換的操作:static_cast,dynamic_cast,reinterpret_cast,const_cast,今天就來聊一聊,在C++之中應該如何來使用這些類型轉換的。

1、舊式類型轉換

開門見山,先聊聊筆者對類型轉換的看法吧。從設計上看,一門面向對象的語言是不一樣提供類型轉換的,這種方式破壞了類型系統。C++為了兼容C也不得不吞下這個苦果,在實際進行編程工作的過程之中,並不太推薦大家使用類型轉換。(Java在這裡就做了一些妥協,在基本類型之中提供了類型轉換。對於對象類型則不提供類型轉換這種黑魔法)

C++之中提供了兩種類型轉換的方式,第一種方式沿用了C語言之中的類型轉換,稱之為舊式類型轉換。說起來也很簡單,舉個慄子:

char x = 'c';
int y = (int) x;

這是最簡單的一個舊式類型轉換,一個char類型被裝換為一個int類型。但是這種舊式的類型轉換是存在問題的:過於粗暴且極易失控,所以C++新提供了四種新式的類型轉換來替代舊式的類型轉換,這四種類型轉換分別用於不用的轉換場景,接下來筆者來一一梳理一下它們的用法。

2、新式的類型轉換

C++語言提供了四種新式類型轉換的操作:
static_cast,dynamic_cast,reinterpret_cast,const_cast,這些操作都依託了C++的模板來使用,標準的用法是

xxx_cast<轉換類型>(轉換參數)

這種新式轉換優於舊式的轉換就在於:編譯器可以在轉換期間進行更多的檢查,對於一些不符合轉換邏輯的轉換進行一些糾錯。而某些類型轉換操作可以利用RTTI(運行時類型信息)來確保類型轉換的合理,這是舊式的類型轉換無法達成的效果。

從名字上就可以看出來,這廝是用來對const屬性進行類型轉換的。這個名字取得有些偏頗,它同樣適用於volatile屬性。它可以為變量添加或接觸上述屬性,它也是新式轉換之中唯一具有這個能力的轉換方式,沒有什麼額外的坑,用戶體驗良好:(但是偶爾對於const屬性的轉換需要執行多步,先通過const_cast轉換,再藉助其他轉換)


atoi(const_cast<const char*>(char_ptr))

static_cast 是靜態的轉換形式,不通過運行時類型檢查來保證轉換的安全性。它主要用於如下場合:

用於基本數據類型之間的轉換,如把long轉換成char,把int轉換成char。

用於面向對象編程之中基類與派生類之間的指針或引用轉換。它分為兩種
上行轉換(把派生類的指針或引用轉換成基類)是安全的;
下行轉換(把基類指針或引用轉換成派生類),由於沒有運行時的動態類型檢查,所以是不安全的。

把非const屬性的類型轉換為const類型,但是不能將const類型轉換為非const類型,這個得藉助前文的const_cast。

void 的空指針轉換成其他類型的的空指針。

上面的幾種概念的比較好理解,這裡筆者著重聊聊上下行轉換:不囉嗦,看代碼:

class Bird {
public:
    virtual void fly() {
        cout << "I can fly." << endl;
    }
};

class Penguin:public Bird {
public:
    void fly() {
        cout << "I can't fly." << endl;
    }
}; 

上述代碼我們定義了兩個類BirdPenguin

int main() {
    Penguin* p = new (std::nothrow) Penguin;
    Bird* b = static_cast<Bird *>(p);

    b->fly();
    return 0;
}

上行轉換,將派生類轉換為基類的指針,合法。

int main() {
    Bird* b = new (std::nothrow) Bird;
    Penguin* p = static_cast<Penguin *>(b);

    if (p != nullptr) {
          p->fly();
     } else {

    }
    return 0;
}

下行轉換,將基類轉換為派生類的指針,此時程序的行為是不確定的。並且編譯期間並沒有警告,這是一種十分危險的用法,所以使用時一定要謹小慎微。所以接下來就要請出下一種轉換dynamic_cast,這是在對象基類和派生類之間轉換推薦的一種方式。

dynamic_cast主要用於在類層次間進行上下行轉換時,它與static_cast的最大的區別就在於dynamic_cast能夠在運行時進行類型檢查的功能,所以做起類型轉換比static_cast更安全,但是dynamic_cast會耗費更多的系統資源。dynamic_cast是無法通過舊式類型轉換完成的類型轉換。

int main() {
    Bird* b = new (std::nothrow) Bird;
    Penguin* p = dynamic_cast<Penguin *>(b);
    if (p != nullptr) {
        p->fly();
    } else {
        cout << "cast failed" << endl;
    }
    return 0;
}

dynamic_cast對於非法的下行轉換會返回空指針,所以可以在一定程度上避免不安全的類型轉換。

reinterpret_cast主要用於指針類型之間的轉換,和對象到指針類型之間的轉換。reinterpret就是對數據的比特位重新解釋轉換為我們需要轉換的對象。其與static_cast的區別在於其能處理有繼承關係類的指針和內置數據類型的轉換。而reinterpret_cast能夠處理所有指針(引用)之間的轉換

int main() {
    Bird* b = new (std::nothrow) Bird;
    Penguin* p = reinterpret_cast<Penguin *>(b);
    if (p != nullptr) {
        p->fly();
    } else {
        cout << "cast failed" << endl;
    }
    return 0;
}

上述代碼依舊可以轉換成功,結果不可控。

3、小結

梳理完C++新引進的四種類型轉換符之後,想必大家在實踐之中可以很好的運用好這些C++的類型轉換。後續筆者還會繼續深入的探討有關C++之中類型系統相關的內容,歡迎大家多多指教。

●編號397,輸入編號直達本文

●輸入m獲取文章目錄

分享C/C++技術文章

相關焦點

  • c++之內存分配、命名空間、強制類型轉換學習總結
    +中的四種強制類型轉換強制類型轉換類型匯總static_castconst_castdynamic_castreinterpret_cast用法:xxx_cast(Expression)2、const_cast強制類型轉換:用於去除變量的只讀屬性強制類型轉換的目標類型必須是指針或者引用代碼解析
  • C/C++中字符串與數字轉換
    ,方法一和方法二是c++中的方法,方法三和方法四是C語言庫函數的方法。方法一:c++11中string中添加了下面這些方法幫助完成字符串和數字的相互轉換stod stof stoi stol stold stoll stoul stoull函數原型:float stof (const string& str, size_t* idx = 0);to_string to_wstring
  • C++的轉換手段並與explicit關鍵詞配合使用
    dynamic_cast,命名上理解是動態類型轉換。如子類和父類之間的多態類型轉換。reinterpret_cast,僅僅重新解釋類型,但沒有進行二進位的轉換。const_cast:const_cast<>可以實現將const 指針類型轉普通指針,const_cast操作不能在不同的種類間轉換。相反,它僅僅把一個它作用的表達式轉換成常量。它可以使一個本來不是const類型的數據轉換成const類型的,或者把const屬性去掉。
  • c++ 之布爾類型和引用的學習總結!
    一、布爾數據類型:在c語言裡面我們知道是沒有布爾數據類型的,而在C++中添加了布爾數據類型(bool),它的取值是:true或者false(也就是1或者0),在內存大小上它佔用一個字節大小:1、bool類型只有true(非0)和false(0)兩個值。
  • 誰說C++ 的強制類型轉換很難懂?
    作者 | 櫻雨樓責編 | 屠敏在上篇與中篇中,我們討論了隱式類型轉換及其與函數重載之間的相關話題。本篇將要討論的即為類型轉換的另一大分支——強制類型轉換。C風格的強制類型轉換在C語言中,強制類型轉換存在兩種等價形式:Type(Value)或(Type)Value。
  • 【C++】數據類型
    今天來講講不c++中同數據類型的使用方法和區別1.基礎類型類型範圍內存中佔的大小char-128~127或0~2551byteunsigned
  • 詳解C++ 的隱式類型轉換與函數重載!
    >涉及底層const的類型轉換被視為最接近精確匹配的隱式類型轉換類型提升優先於算術類型轉換自定義的類型轉換等級最低,且各種自定義類型轉換之間無先後差別我們首先來看一個最簡單的例子:voidtest(int
  • python+C、C++混合編程的應用
    python與C/C++混合編程的本質是python調用C/C++編譯的動態連結庫,關鍵就是把python中的數據類型轉換成c/c++中的數據類型,給編譯函數處理,然後返回參數再轉換成python中的數據類型。
  • 記住這個,能少走彎路,C++兩種隱式類型轉換
    本文將要講解的C++兩種隱式類型轉換,如果不了解其用法,那麼遇到問題的時候,可能就會陷入困境。所以接下來就來講解operator與構造函數的隱式類型轉換,首先通過例子說明operator的隱式類型轉換,再說明構造函數的隱式類型轉換,最後再總結它們之間的區別。
  • 4 種 C++ 強制類型轉換,你都清楚嗎?
    (給CPP開發者加星標,提升C/C++技能)我們先來回憶以下,C 語言的強制類型轉換形式:這種舊式強制類型轉換從表現形式上來說不夠清晰明了
  • C++之字符串類學習總結
    ,所以更加無法獲得字符串類型為了解決這個問題,在c++中,引入了自定義類型,而且可以通過類來完成對字符串類型的定義。那麼C++中的原生類型系統是否包含字符串類型呢?答案是c++中並沒有提供原生的字符串類型。
  • 在C++中將string轉換為char數組
    // string轉換到char數組CPP程序#include <bits/stdc++.h> using namespace std; // 驅動代碼int main() { // 為字符串賦值 string s = "www.linuxmi.com"; int n
  • json for modern c++的使用
    json for modern c++是一款非常好用的json庫,具有語法直觀和使用簡單的特點,並且是用C++11標準編寫的,此外還支持STL和json容器之間的轉換,可謂集方便又強大。本文推薦給廣大C++程式設計師,相信學習完本文之後,在處理json時一定會得心應手。
  • c++11新特性,所有知識點都在這了!
    (kRed == kWhite) { cout << "red == white" << endl; } return 0;}如上代碼,不帶作用域的枚舉類型可以自動轉換成整形,且不同的枚舉可以相互比較,代碼中的紅色居然可以和白色比較,這都是潛在的難以調試的bug,而這種完全可以通過有作用域的枚舉來規避
  • C/C++編程筆記:C語言中的類型轉換,今天教你怎麼寫!
    所謂類型轉換基本上是從一種類型到另一種類型的轉換。類型轉換有兩種類型: 一、隱式類型轉換 通常在表達式中存在多個數據類型時發生。在這種情況下,將進行類型轉換(類型提升)以避免數據丟失。 變量的所有數據類型將升級為具有最大數據類型的變量的數據類型。
  • C 2 C++進階篇(1)
    之前一直是對於面向過程的編程,python有過那種對象風格的編程,但是對於oop的實際開發還停留在表面,沒有獨立的開發c++經驗,也有好幾年沒有碰過c了。由於接手Qt的相關項目,所以對c to c++的進階希望能進行個自我總結。
  • c++的輸入與輸出
    c++輸入與輸出C++ 標準庫提供了一組豐富的輸入/輸出功能,本章將討論 C++ 編程中最基本和最常見的 I/O 操作。輸入輸出並不是c++語言的正式組成成分,c和c++沒有為輸入輸出提供專門的結構。在c語言中輸入輸出是通過調用scanf和printf 實現的,在c++中是通過調用流對象cin和cout實現的。
  • C++、java 和 C 的區別
    一、基礎類型c++:** java:** C#:1.以java為準,c++裡面的int short long 像這樣的整型 一般都有unsigned 和signed的區分 ,這個跟java和c# 的區別比較大,但c#裡面有unit ulong ushort 這三種就相當於c++的修飾詞unsigned,當c++李明的變量類型定義unsigned,就默認是整數。
  • C++ | 虛函數簡介
    本文將簡單探究一下 c++ 中的虛函數實現機制。本文主要基於 vs2013 生成的 32 位代碼進行研究,相信其它編譯器(比如,gcc)的實現大同小異。先從對象大小開始 假設我們有如下代碼,假設 int 佔 4 字節,指針佔 4 字節。
  • Golang 類型斷言 vs 類型轉換
    在Go中,類型斷言和類型轉換是一個令人困惑的事情,他們似乎都在做同樣的事情。這篇文章,我們將看到類型斷言和類型轉換實際上是如何截然不同的,並深入了解在Go中使用它們會發生什麼。但是greeting的類型是固定,並且和初始化期間聲明時的內容一樣。當我們把greeting分配給interface{}類型的時候,請勿修改其原始類型。同樣,當我們斷言類型的時候,我們只是使用了原始類型功能,而不是使用interface公開的有限方法。類型轉換首先,我們花點時間了解一下什麼是 「類型」。