C++11 類型轉(zhuǎn)換

本文根據(jù)眾多互聯(lián)網(wǎng)博客內(nèi)容整理后形成颤诀,引用內(nèi)容的版權(quán)歸原始作者所有,僅限于學(xué)習(xí)研究使用籽暇,不得用于任何商業(yè)用途澈段。

首先回顧一下C++類型轉(zhuǎn)換:C++類型轉(zhuǎn)換分為:隱式類型轉(zhuǎn)換和顯式類型轉(zhuǎn)換

第1部分. 隱式類型轉(zhuǎn)換

又稱為“標(biāo)準(zhǔn)轉(zhuǎn)換”,包括以下幾種情況:

  1. 算術(shù)轉(zhuǎn)換(Arithmetic conversion) : 在混合類型的算術(shù)表達(dá)式中, 最寬的數(shù)據(jù)類型成為目標(biāo)轉(zhuǎn)換類型橄登。
int ival = 3;
double dval = 3.14159;

ival + dval;//ival被提升為double類型

2)一種類型表達(dá)式賦值給另一種類型的對象:目標(biāo)類型是被賦值對象的類型

int *pi = 0; // 0被轉(zhuǎn)化為int *類型
ival = dval; // double->int

例外:void指針賦值給其他指定類型指針時抓歼,不存在標(biāo)準(zhǔn)轉(zhuǎn)換,編譯出錯

3)將一個表達(dá)式作為實(shí)參傳遞給函數(shù)調(diào)用拢锹,此時形參和實(shí)參類型不一致:目標(biāo)轉(zhuǎn)換類型為形參的類型

extern double sqrt(double);
cout << "The square root of 2 is " << sqrt(2) << endl;
//2被提升為double類型:2.0

4)從一個函數(shù)返回一個表達(dá)式谣妻,表達(dá)式類型與返回類型不一致:目標(biāo)轉(zhuǎn)換類型為函數(shù)的返回類型

double difference(int ival1, int ival2)
{
  return ival1 - ival2;    
  //返回值被提升為double類型
}

第2部分. 顯式類型轉(zhuǎn)換

被稱為“強(qiáng)制類型轉(zhuǎn)換”(cast)

C 風(fēng)格 (type-id)
C++風(fēng)格 static_castdynamic_cast面褐、reinterpret_cast拌禾、和const_cast..

關(guān)于強(qiáng)制類型轉(zhuǎn)換的問題,很多書都討論過展哭,寫的最詳細(xì)的是C++ 之父的《C++ 的設(shè)計(jì)和演化》湃窍。最好的解決方法就是不要使用C風(fēng)格的強(qiáng)制類型轉(zhuǎn)換,而是使用標(biāo)準(zhǔn)C++的類型轉(zhuǎn)換符:static_cast, dynamic_cast匪傍。標(biāo)準(zhǔn)C++中有四個類型轉(zhuǎn)換符:static_cast您市、dynamic_castreinterpret_cast役衡、和const_cast茵休。下面對它們一一進(jìn)行介紹。

static_cast

用法:**static_cast **< type-id > ( expression )

說明:該運(yùn)算符把expression轉(zhuǎn)換為type-id類型手蝎,但沒有運(yùn)行時類型檢查來保證轉(zhuǎn)換的安全性榕莺。

來源:為什么需要static_cast強(qiáng)制轉(zhuǎn)換?
1:void*指針->其他類型指針情況
2:改變通常的標(biāo)準(zhǔn)轉(zhuǎn)換情況
3:避免出現(xiàn)可能多種轉(zhuǎn)換的歧義

它主要有如下幾種用法:

  • 用于類層次結(jié)構(gòu)中基類和子類之間指針或引用的轉(zhuǎn)換棵介。進(jìn)行上行轉(zhuǎn)換(把子類的指針或引用轉(zhuǎn)換成基類表示)是安全的钉鸯;進(jìn)行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成子類指針或引用)時,由于沒有動態(tài)類型檢查邮辽,所以是不安全的唠雕。
  • 用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char吨述,把int轉(zhuǎn)換成enum岩睁。這種轉(zhuǎn)換的安全性也要開發(fā)人員來保證。
  • void指針轉(zhuǎn)換成目標(biāo)類型的指針*(不安全!!)
  • 把任何類型的表達(dá)式轉(zhuǎn)換成void*類型揣云。

注意:static_cast不能轉(zhuǎn)換掉expression的const捕儒、volitale、或者_(dá)_unaligned屬性邓夕。

dynamic_cast

用法:**dynamic_cast **< type-id > ( expression )

說明:該運(yùn)算符把expression轉(zhuǎn)換成type-id類型的對象刘莹。Type-id必須是類的指針亿笤、類的引用或者void *;如果type-id是類指針類型栋猖,那么expression也必須是一個指針,如果type-id是一個引用汪榔,那么expression也必須是一個引用蒲拉。

來源:為什么需要dynamic_cast強(qiáng)制轉(zhuǎn)換?
簡單的說痴腌,當(dāng)無法使用virtual函數(shù)的時候

典型案例:X公司提供給我們一個類庫雌团,其中提供一個類Employee.以頭文件Eemployee.h和類庫.lib分發(fā)給用戶顯然我們并無法得到類的實(shí)現(xiàn)的源碼

//Emplyee.h
class Employee 
{
public:    
  virtual int salary();
};

class Manager : public Employee
{
public:     
  int salary();
};

class Programmer : public Employee
{
public:    
  int salary();
};

Y公司在開發(fā)的時候建立有如下類:

class MyCompany
{
public:    
  void payroll(Employee *pe);    
  //...
};

void MyCompany::payroll(Employee *pe)
{    
  //do something
}

但是開發(fā)到后期,我們希望能增加一個bonus()的成員函數(shù)到X公司提供的類層次中士聪。假設(shè)我們知道源代碼的情況下锦援,很簡單,增加虛函數(shù):

//Emplyee.h
class Employee 
{
public:    
  virtual int salary();    
  virtual int bonus();
};

class Manager : public Employee
{
public:     
  int salary();
};

class Programmer : public Employee
{
public:    
  int salary();    
  int bonus();
};

//Emplyee.cpp
...

int Programmer::bonus()
{ 
  //...
}
...   

payroll()通過多態(tài)來調(diào)用bonus()

class MyCompany
{
public:    
  void payroll(Employee *pe);    
};

void MyCompany::payroll(Employee *pe)
{    
  //do something    
  //pe->bonus();
}

但是現(xiàn)在情況是剥悟,我們并不能修改源代碼灵寺,怎么辦?dynamic_cast華麗登場了区岗!
在Employee.h中增加bonus()聲明略板,在另一個地方定義此函數(shù),修改調(diào)用函數(shù)payroll().重新編譯慈缔,ok

//Emplyee.h

class Employee 
{
  public:    
  virtual int salary();
};

class Manager : public Employee
{
  public:     
  int salary();
};

class Programmer : public Employee
{
  public:    
  int salary();    
  int bonus();//直接在這里擴(kuò)展
};

//somewhere.cpp

int Programmer::bonus()
{    
  //define...
}

class MyCompany
{
public:    
  void payroll(Employee *pe);    
  //...
};

void MyCompany::payroll(Employee *pe)
{    
  Programmer *pm = dynamic_cast<Programmer *>(pe);        

  //如果pe實(shí)際指向一個Programmer對象,dynamic_cast成功叮称,并且開始指向Programmer對象起始處    
  if(pm)    
  {        
    //call Programmer::bonus()    
  }    
  //如果pe不是實(shí)際指向Programmer對象,dynamic_cast失敗藐鹤,并且pm = 0    
  else    
  {        
    //use Employee member functions    
  }
}

dynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換瓤檐,還可以用于類之間的交叉轉(zhuǎn)換。在類層次間進(jìn)行上行轉(zhuǎn)換時娱节,dynamic_cast和static_cast的效果是一樣的挠蛉;在進(jìn)行下行轉(zhuǎn)換時,dynamic_cast具有類型檢查的功能括堤,比static_cast更安全碌秸。

class Base
{
public:    
  int m_iNum;    
  virtual void foo();
};

class Derived:public Base
{
public:    
  char *m_szName[100];
};

void func(Base *pb)
{    
  Derived *pd1 = static_cast<Derived *>(pb);    
  Derived *pd2 = dynamic_cast<Derived *>(pb);
}

在上面的代碼段中,
如果pb實(shí)際指向一個Derived類型的對象悄窃,pd1和pd2是一樣的讥电,并且對這兩個指針執(zhí)行Derived類型的任何操作都是安全的;
如果pb實(shí)際指向的是一個Base類型的對象轧抗,那么pd1將是一個指向該對象的指針恩敌,對它進(jìn)行Derived類型的操作將是不安全的(如訪問m_szName),而pd2將是一個空指針(即0横媚,因?yàn)閐ynamic_cast失敗)纠炮。
另外要注意:Base要有虛函數(shù)月趟,否則會編譯出錯;static_cast則沒有這個限制恢口。這是由于運(yùn)行時類型檢查需要運(yùn)行時類型信息孝宗,而這個信息存儲在類的虛函數(shù)表(關(guān)于虛函數(shù)表的概念,詳細(xì)可見<Inside c++ object model>)中耕肩,只有定義了虛函數(shù)的類才有虛函數(shù)表因妇,沒有定義虛函數(shù)的類是沒有虛函數(shù)表的。

另外猿诸,dynamic_cast還支持交叉轉(zhuǎn)換(cross cast)婚被。如下代碼所示。

class Base
{
public:    
  int m_iNum;    
  virtual void f(){}
};

class Derived1 : public Base{};

class Derived2 : public Base{};

void foo(){    
  derived1 *pd1 = new Drived1;    
  pd1->m_iNum = 100;    
  Derived2 *pd2 = static_cast<Derived2 *>(pd1); //compile error    
  Derived2 *pd2 = dynamic_cast<Derived2 *>(pd1); //pd2 is NULL    
  delete pd1;
}

在函數(shù)foo中梳虽,使用static_cast進(jìn)行轉(zhuǎn)換是不被允許的址芯,將在編譯時出錯;而使用 dynamic_cast的轉(zhuǎn)換則是允許的窜觉,結(jié)果是空指針谷炸。

reinpreter_cast

用法:reinpreter_cast<type-id> (expression)

說明:type-id必須是一個指針、引用禀挫、算術(shù)類型淑廊、函數(shù)指針或者成員指針。它可以把一個指針轉(zhuǎn)換成一個整數(shù)特咆,也可以把一個整數(shù)轉(zhuǎn)換成一個指針(先把一個指針轉(zhuǎn)換成一個整數(shù)季惩,在把該整數(shù)轉(zhuǎn)換成原類型的指針,還可以得到原先的指針值)腻格。

IBM的C++指南里倒是明確告訴了我們reinterpret_cast可以画拾,或者說應(yīng)該在什么地方用來作為轉(zhuǎn)換運(yùn)算符:

  • 從指針類型到一個足夠大的整數(shù)類型
  • 從整數(shù)類型或者枚舉類型到指針類型
  • 從一個指向函數(shù)的指針到另一個不同類型的指向函數(shù)的指針
  • 從一個指向?qū)ο蟮闹羔樀搅硪粋€不同類型的指向?qū)ο蟮闹羔?/li>
  • 從一個指向類函數(shù)成員的指針到另一個指向不同類型的函數(shù)成員的指針
  • 從一個指向類數(shù)據(jù)成員的指針到另一個指向不同類型的數(shù)據(jù)成員的指針

const_cast

用法:const_cast<type_id> (expression)

說明:該運(yùn)算符用來修改類型的const或volatile屬性。除了const 或volatile修飾之外菜职, type_id和expression的類型是一樣的青抛。

常量指針被轉(zhuǎn)化成非常量指針,并且仍然指向原來的對象酬核;常量引用被轉(zhuǎn)換成非常量引用蜜另,并且仍然指向原來的對象;常量對象被轉(zhuǎn)換成非常量對象嫡意。

Voiatile和const類試举瑰。舉如下一例:

class B
{
public:
  int m_iNum;
};

void foo()
{
  const B b1;
  b1.m_iNum = 100; //comile error
  B b2 = const_cast<B>(b1);
  b2. m_iNum = 200; //fine
}

上面的代碼編譯時會報(bào)錯,因?yàn)閎1是一個常量對象蔬螟,不能對它進(jìn)行改變此迅;使用const_cast把它轉(zhuǎn)換成一個常量對象,就可以對它的數(shù)據(jù)成員任意改變。注意:b1和b2是兩個不同的對象耸序。

父類子類指針相互轉(zhuǎn)換問題

1:當(dāng)自己的類指針指向自己類的對象時忍些,無論調(diào)用的是虛函數(shù)還是實(shí)函數(shù),其調(diào)用的都是自己的:
2:當(dāng)指向父類對象的父類指針被強(qiáng)制轉(zhuǎn)換成子類指針時候坎怪,子類指針調(diào)用函數(shù)時罢坝,只有非重寫函數(shù)是自己的,虛函數(shù)是父類的搅窿;
3:當(dāng)指向子類對象的子類指針被強(qiáng)制轉(zhuǎn)換成父類指針的時候炸客,也就是父類指針指向子類對象,此時戈钢,父類指針調(diào)用的虛函數(shù)都是子類的,而非虛函數(shù)都是自己的是尔;

將上面三句話總結(jié)成一句話就是:

當(dāng)父類子類有同名非虛函數(shù)的時候殉了,調(diào)用的是轉(zhuǎn)換后的指針類型的函數(shù);
當(dāng)父類子類有同名虛函數(shù)的時候呢拟枚,調(diào)用的是指針轉(zhuǎn)換前指向的對象類型的函數(shù)薪铜。

詳見以下代碼:

#include <iostream>
using namespace std;
class Base {
public:
  virtual void f() { cout << "Base::f" << endl; }
  virtual void g() { cout << "Base::g" << endl; }
  void h() { cout << "Base::h" << endl; }

};
class Derived:public Base
{
public:
  virtual void f(){cout<<"Derived::f"<<endl;}
  void g(){cout<<"Derived::g"<<endl;}
  void h(){cout<<"Derived::h"<<endl;}
};

void main()
{
  Base *pB=new Base();
  cout<<"當(dāng)基類指針指向基類對象時:"<<endl;
  pB->f();
  pB->g();
  pB->h();
  cout<<endl;

  Derived *pD=(Derived*)pB;
  cout<<"當(dāng)父類指針被強(qiáng)制轉(zhuǎn)換成子類指針時:"<<endl;
  pD->f();
  pD->g();
  pD->h();
  cout<<endl;

  Derived *pd=new Derived();
  cout<<"當(dāng)子類指針指向子類時候"<<endl;
  pd->f();
  pd->g();
  pd->h();
  cout<<endl;

  Base *pb=(Base *)pd;
  cout<<"當(dāng)子類指針被強(qiáng)制轉(zhuǎn)換成父類指針時:"<<endl;
  pb->f();
  pb->g();
  pb->h();
  cout<<endl;

  Base *pp=new Derived();
  cout<<"父類指針指向子類對象時候:"<<endl;
  pp->f();
  pp->g();
  pp->h();
  cout<<endl;
}

執(zhí)行結(jié)果如下圖:
202019008467062.png

參考資料

C++標(biāo)準(zhǔn)轉(zhuǎn)換運(yùn)算符reinterpret_cast
c++中四種強(qiáng)制類型轉(zhuǎn)換
【C++專題】static_cast, dynamic_cast, const_cast探討

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恩溅,隨后出現(xiàn)的幾起案子隔箍,更是在濱河造成了極大的恐慌,老刑警劉巖脚乡,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜒滩,死亡現(xiàn)場離奇詭異,居然都是意外死亡奶稠,警方通過查閱死者的電腦和手機(jī)俯艰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锌订,“玉大人竹握,你說我怎么就攤上這事×酒” “怎么了啦辐?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜈项。 經(jīng)常有香客問我芹关,道長,這世上最難降的妖魔是什么紧卒? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任充边,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浇冰。我一直安慰自己贬媒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布肘习。 她就那樣靜靜地躺著际乘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漂佩。 梳的紋絲不亂的頭發(fā)上脖含,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音投蝉,去河邊找鬼养葵。 笑死,一個胖子當(dāng)著我的面吹牛瘩缆,可吹牛的內(nèi)容都是我干的关拒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼庸娱,長吁一口氣:“原來是場噩夢啊……” “哼着绊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熟尉,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤归露,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后斤儿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧包,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年往果,在試婚紗的時候發(fā)現(xiàn)自己被綠了玄捕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡棚放,死狀恐怖枚粘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情飘蚯,我是刑警寧澤馍迄,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站局骤,受9級特大地震影響攀圈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峦甩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一赘来、第九天 我趴在偏房一處隱蔽的房頂上張望现喳。 院中可真熱鬧,春花似錦犬辰、人聲如沸嗦篱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灸促。三九已至,卻和暖如春涵卵,著一層夾襖步出監(jiān)牢的瞬間浴栽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工轿偎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留典鸡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓坏晦,卻偏偏與公主長得像萝玷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子英遭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容