C++強制類型轉換:static_cast灵汪、dynamic_cast、const_cast昼捍、reinterpret_cast

原文地址: https://www.cnblogs.com/chenyangchun/p/6795923.html

1. c強制轉換與c++強制轉換

c語言強制類型轉換主要用于基礎的數(shù)據(jù)類型間的轉換识虚,語法為:

(type-id)expression//轉換格式1

type-id(expression)//轉換格式2

c++除了能使用c語言的強制類型轉換外,還新增了四種強制類型轉換:static_cast妒茬、dynamic_cast担锤、const_cast、reinterpret_cast乍钻,主要運用于繼承關系類間的強制轉化肛循,語法為:

static_cast<new_type>      (expression)
dynamic_cast<new_type>     (expression) 
const_cast<new_type>       (expression) 
reinterpret_cast<new_type> (expression)

備注:new_type為目標數(shù)據(jù)類型铭腕,expression為原始數(shù)據(jù)類型變量或者表達式。

《Effective C++》中將c語言強制類型轉換稱為舊式轉型多糠,c++強制類型轉換稱為新式轉型累舷。

2. C++強制轉換: static_cast、dynamic_cast夹孔、const_cast被盈、reinterpret_cast

static_cast
static_cast相當于傳統(tǒng)的C語言里的強制轉換,該運算符把expression轉換為new_type類型搭伤,用來強迫隱式轉換只怎,例如non-const對象轉為const對象,編譯時檢查怜俐,用于非多態(tài)的轉換身堡,可以轉換指針及其他,但沒有運行時類型檢查來保證轉換的安全性拍鲤。它主要有如下幾種用法:
①用于類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換贴谎。
進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;
進行下行轉換(把基類指針或引用轉換成派生類表示)時季稳,由于沒有動態(tài)類型檢查擅这,所以是不安全的。
②用于基本數(shù)據(jù)類型之間的轉換绞幌,如把int轉換成char蕾哟,把int轉換成enum。這種轉換的安全性也要開發(fā)人員來保證莲蜘。
③把空指針轉換成目標類型的空指針谭确。
④把任何類型的表達式轉換成void類型。
注意:static_cast不能轉換掉expression的const票渠、volatile桩了、或者__unaligned屬性扰藕。

基本類型數(shù)據(jù)轉換舉例如下:

char a = 'a';
int b = static_cast<char>(a);//正確嘲碧,將char型數(shù)據(jù)轉換成int型數(shù)據(jù)

double *c = new double;
void *d = static_cast<void*>(c);//正確冗荸,將double指針轉換成void指針

int e = 10;
const int f = static_cast<const int>(e);//正確,將int型數(shù)據(jù)轉換成const int型數(shù)據(jù)

const int g = 20;
int *h = static_cast<int*>(&g);//編譯錯誤杜窄,static_cast不能轉換掉g的const屬性

類上行和下行轉換:

if(Derived *dp = static_cast<Derived *>(bp)){//下行轉換是不安全的
  //使用dp指向的Derived對象  
}
else{
  //使用bp指向的Base對象  
}

if(Base*bp = static_cast<Derived *>(dp)){//上行轉換是安全的
  //使用bp指向的Derived對象  
}
else{
  //使用dp指向的Base對象  
}

dynamic_cast

dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)

type必須是一個類類型肠骆,在第一種形式中,type必須是一個有效的指針塞耕,在第二種形式中蚀腿,type必須是一個左值,在第三種形式中,type必須是一個右值莉钙。在上面所有形式中廓脆,e的類型必須符合以下三個條件中的任何一個:e的類型是是目標類型type的公有派生類、e的類型是目標type的共有基類或者e的類型就是目標type的的類型磁玉。如果一條dynamic_cast語句的轉換目標是指針類型并且失敗了停忿,則結果為0。如果轉換目標是引用類型并且失敗了蚊伞,則dynamic_cast運算符將拋出一個std::bad_cast異常(該異常定義在typeinfo標準庫頭文件中)席赂。e也可以是一個空指針,結果是所需類型的空指針时迫。

dynamic_cast主要用于類層次間的上行轉換和下行轉換氧枣,還可以用于類之間的交叉轉換(cross cast)。
在類層次間進行上行轉換時别垮,dynamic_cast和static_cast的效果是一樣的;
在進行下行轉換時扎谎,dynamic_cast具有類型檢查的功能碳想,比static_cast更安全。dynamic_cast是唯一無法由舊式語法執(zhí)行的動作毁靶,也是唯一可能耗費重大運行成本的轉型動作胧奔。
(1)指針類型
舉例,Base為包含至少一個虛函數(shù)的基類预吆,Derived是Base的共有派生類龙填,如果有一個指向Base的指針bp,我們可以在運行時將它轉換成指向Derived的指針拐叉,代碼如下:

if(Derived *dp = dynamic_cast<Derived *>(bp)){
  //使用dp指向的Derived對象  
}
else{
  //使用bp指向的Base對象  
}

值得注意的是岩遗,在上述代碼中,if語句中定義了dp凤瘦,這樣做的好處是可以在一個操作中同時完成類型轉換和條件檢查兩項任務宿礁。

(2)引用類型

因為不存在所謂空引用,所以引用類型的dynamic_cast轉換與指針類型不同蔬芥,在引用轉換失敗時梆靖,會拋出std::bad_cast異常,該異常定義在頭文件typeinfo中笔诵。

void f(const Base &b){
 try{
   const Derived &d = dynamic_cast<const Base &>(b);  
   //使用b引用的Derived對象
 }
 catch(std::bad_cast){
   //處理類型轉換失敗的情況
 }
}

const_cast
const_cast返吻,用于修改類型的const或volatile屬性。

該運算符用來修改類型的const(唯一有此能力的C++-style轉型操作符)或volatile屬性乎婿。除了const 或volatile修飾之外测僵, new_type和expression的類型是一樣的。
①常量指針被轉化成非常量的指針次酌,并且仍然指向原來的對象恨课;
②常量引用被轉換成非常量的引用舆乔,并且仍然指向原來的對象;
③const_cast一般用于修改底指針剂公。如const char *p形式希俩。

舉例轉換如下:

const int g = 20;
int *h = const_cast<int*>(&g);//去掉const常量const屬性

const int g = 20;
int &h = const_cast<int &>(g);//去掉const引用const屬性

 const char *g = "hello";
char *h = const_cast<char *>(g);//去掉const指針const屬性

reinterpret_cast
new_type必須是一個指針、引用纲辽、算術類型颜武、函數(shù)指針或者成員指針。它可以把一個指針轉換成一個整數(shù)拖吼,也可以把一個整數(shù)轉換成一個指針(先把一個指針轉換成一個整數(shù)鳞上,再把該整數(shù)轉換成原類型的指針,還可以得到原先的指針值)吊档。

reinterpret_cast意圖執(zhí)行低級轉型篙议,實際動作(及結果)可能取決于編輯器,這也就表示它不可移植怠硼。

舉一個錯誤使用reintepret_cast例子鬼贱,將整數(shù)類型轉換成函數(shù)指針后,vc++在執(zhí)行過程中會報"...中的 0xxxxxxxxx 處有未經處理的異常: 0xC0000005: Access violation"錯誤:

#include <iostream>
using namespace std;
int output(int p){
    cout << p <<endl;
  return 0;
}

typedef int (*test_func)(int );//定義函數(shù)指針test_func
int main(){
    int p = 10;
    test_func fun1 = output;
    fun1(p);//正確
    test_func fun2 = reinterpret_cast<test_func>(&p);
    fun2(p);//...處有未經處理的異常: 0xC0000005: Access violation
    return 0;
}

IBM的C++指南香璃、C++之父也都指出:錯誤的使用reinterpret_cast很容易導致程序的不安全这难,只有將轉換后的類型值轉換回到其原始類型,這樣才是正確使用reinterpret_cast方式葡秒。

MSDN中也提到了姻乓,實際中可將reinterpret_cast應用到哈希函數(shù)中,如下(64位系統(tǒng)中需將unsigned int修改為unsigned long):

// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>

// Returns a hash code based on an address
unsigned short Hash( void *p ) {
   unsigned int val = reinterpret_cast<unsigned int>( p );
   return ( unsigned short )( val ^ (val >> 16));
}

using namespace std;
int main() {
   int a[20];
   for ( int i = 0; i < 20; i++ )
      cout << Hash( a + i ) << endl;
}

另外眯牧,static_cast和reinterpret_cast的區(qū)別主要在于多重繼承蹋岩,比如

class A {
    public:
    int m_a;
};
 
class B {
    public:
    int m_b;
};
 
class C : public A, public B {};

那么對于以下代碼:

C c;
printf("%p, %p, %p", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));

前兩個的輸出值是相同的,最后一個則會在原基礎上偏移4個字節(jié)学少,這是因為static_cast計算了父子類指針轉換的偏移量星澳,并將之轉換到正確的地址(c里面有m_a,m_b,轉換為B*指針后指到m_b處)旱易,而reinterpret_cast卻不會做這一層轉換禁偎。
 因此, 你需要謹慎使用 reinterpret_cast。

  1. c++強制轉換注意事項
    新式轉換較舊式轉換更受歡迎阀坏。原因有二如暖,一是新式轉型較易辨別,能簡化“找出類型系統(tǒng)在哪個地方被破壞”的過程忌堂;二是各轉型動作的目標愈窄化盒至,編譯器愈能診斷出錯誤的運用。
    盡量少使用轉型操作,尤其是dynamic_cast枷遂,耗時較高樱衷,會導致性能的下降,盡量使用其他方法替代酒唉。

參考資料:

a):http://en.cppreference.com/w/cpp/language/static_cast

b):http://en.cppreference.com/w/cpp/language/dynamic_cast

c):http://en.cppreference.com/w/cpp/language/const_cast

d):http://en.cppreference.com/w/cpp/language/reinterpret_cast

e):《Effective C++》條款27:盡量少做轉型動作

f): 百度百科

g) 《C++ Primer》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末矩桂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痪伦,更是在濱河造成了極大的恐慌侄榴,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件网沾,死亡現(xiàn)場離奇詭異癞蚕,居然都是意外死亡,警方通過查閱死者的電腦和手機辉哥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門桦山,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人醋旦,你說我怎么就攤上這事度苔。” “怎么了浑度?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鸦概。 經常有香客問我箩张,道長,這世上最難降的妖魔是什么窗市? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任先慷,我火速辦了婚禮,結果婚禮上咨察,老公的妹妹穿的比我還像新娘论熙。我一直安慰自己,他們只是感情好摄狱,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布脓诡。 她就那樣靜靜地躺著,像睡著了一般媒役。 火紅的嫁衣襯著肌膚如雪祝谚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天酣衷,我揣著相機與錄音交惯,去河邊找鬼。 笑死,一個胖子當著我的面吹牛席爽,可吹牛的內容都是我干的意荤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼只锻,長吁一口氣:“原來是場噩夢啊……” “哼玖像!你這毒婦竟也來了?” 一聲冷哼從身側響起炬藤,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤御铃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沈矿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上真,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年羹膳,在試婚紗的時候發(fā)現(xiàn)自己被綠了睡互。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡陵像,死狀恐怖就珠,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情醒颖,我是刑警寧澤妻怎,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站泞歉,受9級特大地震影響逼侦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜腰耙,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一榛丢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挺庞,春花似錦晰赞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至援制,卻和暖如春锨用,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背隘谣。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工增拥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啄巧,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓掌栅,卻偏偏與公主長得像秩仆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猾封,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348