C++類型強制轉(zhuǎn)換

類型轉(zhuǎn)換

  • 隱式類型轉(zhuǎn)換
  • 顯式類型轉(zhuǎn)換

語法

xxx_cast <類型> (表達式)

1. static_cast

  • 用法
    用于非多態(tài)類型之間的轉(zhuǎn)換,不提供運行時的檢查來確保轉(zhuǎn)換的安全性。
    主要有如下
  1. 基本數(shù)據(jù)類型轉(zhuǎn)換
  2. int轉(zhuǎn)換成enum
  3. 基類和子類之間指針和引用的轉(zhuǎn)換
  • 上行轉(zhuǎn)換掸刊,把子類的指針或引用轉(zhuǎn)換成父類,這種轉(zhuǎn)換是安全的(通常使用默認轉(zhuǎn)換)泞歉。
  • 下行轉(zhuǎn)換锰提,把父類的指針或引用轉(zhuǎn)換成子類,這種轉(zhuǎn)換是不安全的阳准,也需要程序員來保證(通常使用dynamic_cast)氛堕。

1.1 基本數(shù)據(jù)類型轉(zhuǎn)換

  • int轉(zhuǎn)換成char
int n = 97;
cout << n << '\t' << (char)n << '\t' << static_cast<char>(n) << endl;
  • int轉(zhuǎn)換成float
int n = 1;
cout << n/2 << '\t' << (float)n/2 << '\t' << static_cast<float>(n)/2 << endl;

1.2 int轉(zhuǎn)換成enum

enum Week{
   SUN,MON,TUE,WED,THU,FRI,SAT
};
Week day = 0;

編譯上述代碼出現(xiàn)如下錯誤:

  • g++編譯錯誤:error: invalid conversion from ‘int’ to ‘Week’
  • clang++編譯錯誤:error: cannot initialize a variable of type 'const Week' with an rvalue of type 'int'

把代碼Week day = 0;改為Week day = static_cast<Week>(0);可以消除上面的錯誤。

1.3 基類和子類之間指針和引用的轉(zhuǎn)換

已知存在繼承關系的兩個類BaseDerive野蝇。

class Base{
public:
    void Print(){cout << "Base" << endl;}
};
class Derive:public Base{
public:
    void Print(){cout << "Derive" << endl;}
};
1.3.1 上行轉(zhuǎn)換
// 對象
Derive d;
Base b = static_cast<Base>(d);
b.Print();

// 引用
Base& fb = static_cast<Base&>(d);
fb.Print();

// 指針
Base* pb = static_cast<Base*>(new Derive);
pb->Print();

通常使用隱式轉(zhuǎn)換

// 對象
Derive d;
Base b = d;
b.Print();

// 引用
Base& fb = d;
fb.Print();

// 指針
Base* pb = new Derive;
pb->Print();
1.3.2 下行轉(zhuǎn)換

這種轉(zhuǎn)換不安全讼稚,通常使用dynamic_cast括儒。

1.4 指針/引用轉(zhuǎn)換

void指針轉(zhuǎn)換成目標類型的指針,這種轉(zhuǎn)換是不安全的锐想,也需要程序員來保證帮寻;
如下代碼編譯出錯,因為不能把void*轉(zhuǎn)換成其他具體指針赠摇。

void* pd = new Derive;
Base* pb = pd; //  error: invalid conversion from ‘void*’ to ‘Base*’
pb->Print();

Base* pb = pd;改為Base* pb = static_cast<Base*>(pd);固逗,可以解決上述編譯錯誤。

static_cast跟傳統(tǒng)小括號轉(zhuǎn)換方式幾乎是一致的藕帜。
問題:為什么要用static_cast代替?zhèn)鹘y(tǒng)小括號轉(zhuǎn)換方式烫罩?
(來自《C++ prime 4th》5.12.7)
標準 C++ 為了加強類型轉(zhuǎn)換的可視性,引入命名的強制轉(zhuǎn)換操作符洽故,為程序員在必須使用強制轉(zhuǎn)換時提供了更好的工具贝攒。例如, 非指針的 static_cast 和const_cast 要比 reinterpret_cast 更安全时甚。結(jié)果使程序員(以及讀者和操縱程序的工具)可清楚地辨別代碼中每個顯式的強制轉(zhuǎn)換潛在的風險級別隘弊。
雖然標準 C++ 仍然支持舊式強制轉(zhuǎn)換符號,但是我們建議荒适,只有在 C 語言或標準 C++ 之前的編譯器上編寫代碼時梨熙,才使用這種語法。

2. const_cast

常量賦值給非常量時吻贿,會出現(xiàn)下面編譯錯誤。

const int a = 10;

// 指針
const int* cp = &a;
int* p = cp;// error: invalid conversion from ‘const int*’ to ‘int*’

// 引用
const int& cf = a;
int& f = cf;// error: binding ‘const int’ to reference of type ‘int&’ discards qualifiers

const_cast主要作用是移除類型的const屬性哑子。

  1. 常量指針被轉(zhuǎn)化成非常量的指針舅列,并且仍然指向原來的對象;
  2. 常量引用被轉(zhuǎn)換成非常量的引用卧蜓,并且仍然指向原來的對象帐要;
  3. const_cast一般用于修改底指針。如const char *p形式弥奸。
const int a = 10;

// 指針
const int* cp = &a;
int* p = const_cast<int*>(cp);

// 引用
const int& cf = a;
int& f = const_cast<int&>(cf);
  • 在類中的應用情景
    1.const對象想使用類中非const成員函數(shù)
    2.const成員函數(shù)中想修改某個成員變量
#include <iostream>

using namespace std;
//const成員函數(shù)修改const成員變量的兩種方法
//1.使用const_cast
//2.成員變量前加關鍵字mutable
class Test
{
    int m_n;
    int m_count;
    //mutable允許成員變量在const成員函數(shù)中被改變
public:
    Test(int n):m_n(n),m_count(0){}
    void Add(int m)
    {
        m_n = m+m_n;
    }
    void Print()const
    {
        //在const函數(shù)中this指針是const榨惠,所以this指針下的成員都是const
        cout<<m_n<<endl;
        //cout<<"count= "<<++m_count<<endl;//這一句編譯會報錯,錯誤:increment of member ‘Test::m_count’ in read-only object

        cout<<"count= "<<++const_cast<int&>(m_count)<<endl;//在const成員函數(shù)中修改輔助的成員變量
    }
    void PrintCount()const {cout<<m_count<<endl;}
};
int main()
{
    //const_cast<>() :把const對象/指針/引用轉(zhuǎn)成非const對象/指針/引用
    const Test t(10);
    t.Print();

    //t.Add(5);//這一句編譯會報錯盛霎,因為const對象只能調(diào)用const成員函數(shù) 
    //使用const_cast<>()將t轉(zhuǎn)為非const對象
    const_cast<Test&>(t).Add(5);
    t.Print();
    t.PrintCount();
}

3. dynamic_cast

用于類的指針赠橙、類的引用或者void *轉(zhuǎn)化。

主要用于以下三種情況:

  1. 上行轉(zhuǎn)換愤炸,把子類的指針或引用轉(zhuǎn)換成父類期揪,與static_cast相同。
  2. 下行轉(zhuǎn)換规个,把父類的指針或引用轉(zhuǎn)換成子類凤薛,比static_cast安全姓建。
  3. 交叉轉(zhuǎn)換,兄弟之間指針轉(zhuǎn)換缤苫,static_cast會出現(xiàn)編譯錯誤速兔。

如果時指針,進行正確的轉(zhuǎn)換活玲,獲得對應的值涣狗;否則返回NULL,如果是引用翼虫,則在運行時就會拋出異常屑柔;

  • dynamic_cast使用條件
    1.存在父子關系
    2.父類中要存在多態(tài)
    3.子類指針/引用指向父類指針/引用

下行轉(zhuǎn)換

#include <iostream>

using namespace std;

class Base {
public:
  void Print() { cout << "Base" << endl; }
  virtual ~Base(){}
};
class Derive : public Base {
public:
  void Print() { cout << "Derive" << endl; }
};
int main() { 
    Base * pB = new Derive;
    pB->Print();
    Derive *pD = dynamic_cast<Derive*>(pB);
    pD->Print();
}

交叉轉(zhuǎn)換

#include <iostream>

using namespace std;

class Base {
public:
  void Print() { cout << "Base" << endl; }
  virtual ~Base(){}
};
class Derive1 : public Base {
public:
  void Print() { cout << "Derive1" << endl; }
};
class Derive2 : public Base {
public:
  void Print() { cout << "Derive" << endl; }
};

int main() { 
    Derive1* pD1 = new Derive1;
    pD1->Print();
    Derive2 *pD2 = dynamic_cast<Derive2*>(pD1);
    pD2->Print();
}
  • dynamic_cast使用場景
    1.如果我們想使用父類指針/引用調(diào)用子類特有的成員函數(shù)
    2.可以通過返回值來判斷繼承關系(假設你定義了貓,狗珍剑,豬三個純虛函數(shù)掸宛,并且貓有1000種,狗有1000種招拙,豬有1000種唧瘾,在狗的類中,都有“吠”這個成員函數(shù)别凤,現(xiàn)在你想通過代碼讓所有的狗叫饰序,你不可能定義1000個狗的對象,那樣太麻煩了规哪,現(xiàn)在的問題是怎么做才能從3000個物種種得到你的1000只狗求豫,這樣就可以使用返回值來判斷,假設Dog是所有狗的基類,vec是所有3000種動物的集合)
Dog *g = new Dog;
foreach(auto i : vec)
{
    if(dynamic_cast<i*>(g) != NULL)
        g->func();
}

由于dynamic_cast轉(zhuǎn)化失敗時诉稍,會返回空值蝠嘉,這樣不是繼承于Dog的類都不會調(diào)用到func()這個函數(shù),只有Dog派生出來的類才會調(diào)用杯巨。

擴展問題

  • 如果不是多態(tài)會有什么情況蚤告?編譯錯誤
  • dynamic_cast為什么只在多態(tài)的繼承關系才有效?由于運行時類型檢查需要運行時類型信息服爷,而這個信息存儲在類的虛函數(shù)表杜恰。

4. reinterpret_cast

修改了操作數(shù)類型,重新解釋了給出的對象的比特模型而沒有進行二進制轉(zhuǎn)換。

主要用于以下六種情況:

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

4.1 指針類型與整數(shù)類型的轉(zhuǎn)化

把指針值(地址)轉(zhuǎn)化成整數(shù)仍源,把整數(shù)轉(zhuǎn)化成指針值心褐。

#include <iostream>
#include <vector>

using namespace std;

void Func(){
  cout << "Func" << endl;
}

int main() {
  // 變量指針類型與整數(shù)類型轉(zhuǎn)化
  {
    int n = 100;
    int addr = reinterpret_cast<int>(&n);
    cout << "addr:" << hex << addr << dec << " "<< &n << endl;
    int* b = reinterpret_cast<int*>(addr);
    cout << "value:" << *b << endl;
  }
  // 函數(shù)指針類型與整數(shù)類型轉(zhuǎn)化
  {
    int f = reinterpret_cast<int>(Func);
    typedef void (*pFunc)();
    pFunc pf = reinterpret_cast<pFunc>(f);
    pf();
  }
}

4.2 函數(shù)指針的轉(zhuǎn)化

FuncNum函數(shù)指針轉(zhuǎn)化成FuncAddr,使用FuncAddr的參數(shù)列表笼踩。

#include <iostream>
#include <vector>

using namespace std;

void FuncNum(int n){
  cout << "num:" << n << endl;
}
void FuncAddr(int* p) { 
  cout << "addr:" << p << endl;
}


int main() {
  int n = 10;
  FuncNum(n);
  FuncAddr(&n);
  
  typedef void (*pfNum)(int n);
  typedef void (*pfAddr)(int* n);
  pfNum pfunc = FuncNum;
  pfunc(n);
  reinterpret_cast<pfAddr>(pfunc)(&n);
}

4.3 對象指針的轉(zhuǎn)化

A類對象指針轉(zhuǎn)化成B類的對象指針檬寂,使用B類的成員函數(shù)。

#include <iostream>
#include <vector>

using namespace std;

class A{
public:
  void Func(){
    cout << "A" << endl;
  }
};

class B {
public:
  void Test() { cout << "B" << endl; }
};

int main() {
  A* pA = new A;
  pA->Func();
  B* pB = reinterpret_cast<B*>(pA);
  pB->Test();
}

4.4 類函數(shù)成員的轉(zhuǎn)化

A類對象使用B類的成員變量戳表。(A類與B類沒有任何關系桶至。)

#include <iostream>
#include <vector>

using namespace std;

class A{
public:
  void Func(){
    cout << "A" << endl;
  }
};

class B {
public:
  void Test() { cout << "B" << endl; }
};

int main() {
  // 對象的函數(shù)指針
  {
    A a;
    a.Func();
    typedef void (A::*Func_t)();
    Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
    (a.*pfTest)();
  }
  // 對象指針的函數(shù)指針
  {
    A *pA = new A;
    pA->Func();
    typedef void (A::*Func_t)();
    Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
    (pA->*pfTest)();
  }
}

4.5 類數(shù)據(jù)成員的轉(zhuǎn)化

#include <iostream>
#include <vector>

using namespace std;

class Test{
public:
  Test(int data) : data(data) {}
private:
  int data;
};
int main() {
  Test test(0x61626364);
  char* p = reinterpret_cast<char*>(&test);
  for (int i = 0; i != sizeof(Test) / sizeof(char); i++) {
    cout << p[i] << endl;
  }
}

謹慎使用reinterpret_cast


小結(jié)

No. 轉(zhuǎn)換 轉(zhuǎn)換對象 作用 轉(zhuǎn)換時機
1 static_cast 基本類型昼伴、指針、引用 實現(xiàn)傳統(tǒng)的小括號轉(zhuǎn)化功能 在編譯期間實現(xiàn)轉(zhuǎn)換
2 const_cast const類型的對象镣屹、指針圃郊、引用 移除變量const限定 在編譯期間實現(xiàn)轉(zhuǎn)換
3 dynamic_cast 類的指針、類的引用或者void * 多態(tài)父類指針/引用轉(zhuǎn)化成子類指針/引用 在運行期間實現(xiàn)轉(zhuǎn)換女蜈,并可以返回轉(zhuǎn)換成功與否的標志/拋出異常
4 reinterpret_cast 指針持舆、引用、算術類型 萬能強制類型轉(zhuǎn)換 在編譯期間實現(xiàn)轉(zhuǎn)換

擴展閱讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伪窖,一起剝皮案震驚了整個濱河市逸寓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌覆山,老刑警劉巖竹伸,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異簇宽,居然都是意外死亡勋篓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門魏割,熙熙樓的掌柜王于貴愁眉苦臉地迎上來譬嚣,“玉大人,你說我怎么就攤上這事钞它“菀” “怎么了?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵遭垛,是天一觀的道長尼桶。 經(jīng)常有香客問我,道長耻卡,這世上最難降的妖魔是什么疯汁? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任牲尺,我火速辦了婚禮卵酪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谤碳。我一直安慰自己溃卡,他們只是感情好,可當我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布蜒简。 她就那樣靜靜地躺著瘸羡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搓茬。 梳的紋絲不亂的頭發(fā)上犹赖,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天队他,我揣著相機與錄音,去河邊找鬼峻村。 笑死麸折,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的粘昨。 我是一名探鬼主播垢啼,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼张肾!你這毒婦竟也來了芭析?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤吞瞪,失蹤者是張志新(化名)和其女友劉穎馁启,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尸饺,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡进统,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浪听。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片螟碎。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖迹栓,靈堂內(nèi)的尸體忽然破棺而出掉分,到底是詐尸還是另有隱情,我是刑警寧澤克伊,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布酥郭,位于F島的核電站,受9級特大地震影響愿吹,放射性物質(zhì)發(fā)生泄漏不从。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一犁跪、第九天 我趴在偏房一處隱蔽的房頂上張望椿息。 院中可真熱鬧,春花似錦坷衍、人聲如沸寝优。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乏矾。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钻心,已是汗流浹背凄硼。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捷沸,地道東北人帆喇。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像亿胸,于是被迫代替她去往敵國和親坯钦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,435評論 2 348

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