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

數(shù)據(jù)類型是對內(nèi)存的抽象椅野,在實際的開發(fā)過程中氮发,我們常常會遇到把一種類型轉(zhuǎn)換成另外一種類型的情況。

那么砸捏,在C/C++中谬运,類型轉(zhuǎn)換都有哪些玩法呢?

C 的類型轉(zhuǎn)換

寫法:(type_name) expression

#include <stdio.h>
main() {
    double d = (double) 5;
    printf("%f\n", d);
    int i = (int) 5.4; // OK
    printf("%d\n", i);
    int i2 = (int) (5.4); // OK
    printf("%d\n", i2);
    int i3 = int (5.4); // Error
    printf("%d\n", i3);
    double result = (double) 4 / 5; // OK
    printf("%f\n", result);
}

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

把一種數(shù)據(jù)類型轉(zhuǎn)換成另外一種數(shù)據(jù)類型垦藏,可以是顯式的梆暖,也可以是隱式的。

C++中的隱式轉(zhuǎn)換

隱式轉(zhuǎn)換不需要任何轉(zhuǎn)換運算符膝藕。

他們通常自動發(fā)生在將一個類型的值拷貝給另外一個兼容的類型時式廷。

我們來看一個例子:

#include <iostream>
class A {
    int x = 0;
public:
    A(int x) { this->x = x; }
    int getX() { return x; }
};
class B {
    int x = 0, y = 0;
public:
    B(A a) { x = a.getX(); y = a.getX(); }
    void getXY(int &x, int &y) { x = this->x; y = this->y; }
};
int main() {
    A a(10);
    B b = a; // 隱式轉(zhuǎn)換
    int x, y;
    b.getXY(x, y);
    std::cout << "x: " << x << " y: " << y << std::endl;
    return 0;
}

B b = a;處發(fā)生了隱式轉(zhuǎn)換,程序輸出是

x: 10 y: 10

C++ 關(guān)鍵詞 explicit

C++11之后引入了關(guān)鍵詞explicit芭挽,如果加了explicit, 將不允許 隱式轉(zhuǎn)換復(fù)制初始化.

我們?nèi)绻谏厦娴睦又屑尤雃xplicit

explicit B(A a) { x = a.getX(); y = a.getX(); }

將會發(fā)生編譯錯誤:

error: conversion from 'A' to non-scalar type 'B' requested

C++中的顯示轉(zhuǎn)換

我們可以像C一樣的方式進行強制轉(zhuǎn)換

B b = (B) a;
B b = B (a); 

像上面一樣直接強轉(zhuǎn)而不使用轉(zhuǎn)換運算符滑废,有啥壞處呢?

它可能導(dǎo)致代碼編譯通過運行出錯袜爪。就像下面的例子:

#include <iostream>
class Data { float i = 5, j = 6; };
class Addition {
   int x, y;
public:
   Addition(int x, int y) { this->x = x; this->y = y; }
   int result() { return x + y; }
};
int main() {
    Data d;
    Addition *add = (Addition *) &d;
    std::cout << add->result();
    return 0;
}

幾種轉(zhuǎn)換運算符

傳統(tǒng)的顯式類型轉(zhuǎn)換允許將任何指針轉(zhuǎn)換為任何其他指針類型蠕趁,而與它們指向的類型無關(guān)。這可能導(dǎo)致運行錯誤或其他意外的結(jié)果辛馆。

為了更好的控制類型轉(zhuǎn)換俺陋,C++提供了四種基礎(chǔ)的轉(zhuǎn)換運算符:

static_cast, const_cast, dynamic_cast, reinterpret_cast.

static_cast

static_cast通常用于轉(zhuǎn)換非多態(tài)類型

static_cast轉(zhuǎn)換運算符可以用來執(zhí)行任何隱式轉(zhuǎn)換昙篙,包括標準類型和用戶定義的類型腊状。就像下面這樣:

typedef unsigned char BYTE;  
void f() {  
   int i = 65;  
   float f = 2.5;  
   char ch = static_cast<char>(i);   // int to char  
   double dbl = static_cast<double>(f);   // float to double  
   i = static_cast<BYTE>(ch);  
} 

static_cast轉(zhuǎn)換運算符可以將一個整型數(shù)據(jù)轉(zhuǎn)換成枚舉類型。如果這個整型數(shù)據(jù)超過了枚舉定義的范圍苔可,那么轉(zhuǎn)換的結(jié)果是未定義的缴挖。

enum Status {
    On = 0,
    Off
};
Status s = static_cast<Status>(3);

你也可以使用static_cast將子類轉(zhuǎn)換成基類,也可以將基類轉(zhuǎn)換成子類焚辅。但將基類轉(zhuǎn)換成子類是不安全的映屋。

class B {};    
class D : public B {};  
void f(B* pb, D* pd) {  
   D* pd2 = static_cast<D*>(pb);   // 不安全苟鸯,D可能有B沒有的數(shù)據(jù)或方法  
   B* pb2 = static_cast<B*>(pd);   // 安全
}  

const_cast

const_cast用來刪除const屬性。

#include <iostream>
class B {
public:
    int num = 5;
};
int main() {
    using namespace std;
    const B b1;

    b1.num = 10; // Error: assignment of member 'B::num' in read-only object
    cout << b1.num << endl;

    B b2 = const_cast<B>(b1); // Error: invalid use of const_cast with type 'B', 
                              // which is not a pointer, reference, nor a pointer-to-data-member type
    b2.num = 15;
    cout << b1.num << endl;

    B *b3 = const_cast<B *>(&b1); // OK 
    b3->num = 20;
    cout << b1.num << endl;

    B &b4 = const_cast<B &>(b1); // OK
    b4.num = 25;
    cout << b1.num << endl;
}

另外一個MSDN上的例子:

#include <iostream>
using namespace std;
class CCTest {
public:
   void setNumber( int );
   void printNumber() const;
private:
   int number;
};

void CCTest::setNumber( int num ) { number = num; }

void CCTest::printNumber() const {
   cout << "\nBefore: " << number;
   const_cast< CCTest * >( this )->number--;
   cout << "\nAfter: " << number;
}

int main() {
   CCTest X;
   X.setNumber( 8 );
   X.printNumber();
}

在第二個例子中棚点,我們也可以使用mutable關(guān)鍵字來進行處理早处。

dynamic_cast

dynamic_cast 通常用于轉(zhuǎn)換多態(tài)類型

其目的是確保類型轉(zhuǎn)換的結(jié)果是所請求類的有效完整對象瘫析。

將子類轉(zhuǎn)換成基類時砌梆,dynamic_cast 都是成功的。

class B { };  
class C : public B { };  
class D : public C { };  
  
void f(D* pd) {  
   C* pc = dynamic_cast<C*>(pd);   // ok: C is a direct base class  
                                   // pc points to C subobject of pd   
   B* pb = dynamic_cast<B*>(pd);   // ok: B is an indirect base class  
                                   // pb points to B subobject of pd  
} 

上面例子的這種轉(zhuǎn)換我們通常叫做向上轉(zhuǎn)換贬循,向上轉(zhuǎn)換是一種隱式轉(zhuǎn)換么库。因此這種情況下,我們使用static_cast或者dynamic_cast都是一樣的甘有。

我們再來看一個向下轉(zhuǎn)換(將基類轉(zhuǎn)換成子類)的例子:

#include <iostream>
class B {virtual void f(){}};
class D : public B {};

int main() {
    B* b = new D;
    B* b2 = new B;

    D* d = dynamic_cast<D*>(b);
    if (d == nullptr) {
        std::cout << "null pointer on first type-casting." << std::endl;
    }
    D* d2 = dynamic_cast<D*>(b2);
    if (d2 == nullptr) {
        std::cout << "null pointer on second type-casting." << std::endl;
    }
    return 0;
}

顯然诉儒,輸出結(jié)果是

null pointer on second type-casting.

再來看一個子類之間轉(zhuǎn)換的例子:

#include <iostream>
class A {
public:
    int num = 5;
    virtual void f(){}
};

class B : public A {};

class D : public A {};

int main() {
    B *b = new B;
    b->num = 10;
    D *d1 = static_cast<D *>(b); // Error: invalid static_cast from type 'B*' to type 'D*'
    D *d2 = dynamic_cast<D *>(b); // OK
    std::cout << (d2 == nullptr) << std::endl; // 1
    delete b;
}

一個比較復(fù)雜的例子:

class A {virtual void f();};  
class B : public A {virtual void f();};  
class C : public A { };  
class D {virtual void f();};  
class E : public B, public C, public D {virtual void f();};  
  
void f(D* pd) {  
   E* pe = dynamic_cast<E*>(pd);  
   B* pb = pe;   // upcast, implicit conversion  
   A* pa = pb;   // upcast, implicit conversion  
}  

reinterpret_cast

reinterpret_cast 用來進行強制的數(shù)據(jù)轉(zhuǎn)換(內(nèi)存的重新解釋),不管他們轉(zhuǎn)換的類型之間有沒有關(guān)聯(lián)亏掀。

class A {};
class B {};
A *a = new A;
B *b = reinterpret_cast<B*>(a);

誤用 reinterpret_cast 操作符很容易變得不安全忱反。 除非所需的轉(zhuǎn)換本質(zhì)上是低級的,否則你應(yīng)該使用其他強制轉(zhuǎn)換運算符滤愕。

但是温算,它不能刪除const屬性。

class A {};
int main() {
    const A a;
    A *b = reinterpret_cast<A*>(&a); // Error: reinterpret_cast from type 'const A*' to type 'A*' casts away qualifiers
}

思考一下

  1. 下面的例子中间影,哪些地方會有編譯錯誤注竿?

    class A {
    public:
        virtual void foo() { }
    };
    class B {
    public:
        virtual void foo() { }
    };
    class C : public A , public B {
    public:
        virtual void foo() { }
    };
    void bar1(A *pa) {
        B *pc = dynamic_cast<B*>(pa);
    }
    void bar2(A *pa) {
        B *pc = static_cast<B*>(pa);
    }
    void bar3() {
        C c;
        A *pa = &c;
        B *pb = static_cast<B*>(static_cast<C*>(pa));
    }
    int main() {
        return 0;
    }
    
  2. 下面的模板函數(shù)是安全的嗎?如果不是魂贬,更好的做法是什么巩割?

    template <class T>
    unsigned char* alias(T& x) {
        return (unsigned char*)(&x);
    }
    

Reference

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市付燥,隨后出現(xiàn)的幾起案子宣谈,更是在濱河造成了極大的恐慌,老刑警劉巖键科,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻丑,死亡現(xiàn)場離奇詭異,居然都是意外死亡勋颖,警方通過查閱死者的電腦和手機嗦嗡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饭玲,“玉大人侥祭,你說我怎么就攤上這事。” “怎么了卑硫?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚕断。 經(jīng)常有香客問我欢伏,道長,這世上最難降的妖魔是什么亿乳? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任硝拧,我火速辦了婚禮,結(jié)果婚禮上葛假,老公的妹妹穿的比我還像新娘障陶。我一直安慰自己,他們只是感情好聊训,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布抱究。 她就那樣靜靜地躺著带斑,像睡著了一般鼓寺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勋磕,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天妈候,我揣著相機與錄音,去河邊找鬼挂滓。 笑死苦银,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赶站。 我是一名探鬼主播幔虏,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼含思,長吁一口氣:“原來是場噩夢啊……” “哼礼旅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尘颓,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤团秽,失蹤者是張志新(化名)和其女友劉穎主胧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體习勤,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡踪栋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了图毕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夷都。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖予颤,靈堂內(nèi)的尸體忽然破棺而出囤官,到底是詐尸還是另有隱情冬阳,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布党饮,位于F島的核電站肝陪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刑顺。R本人自食惡果不足惜氯窍,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹲堂。 院中可真熱鬧狼讨,春花似錦、人聲如沸柒竞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朽基。三九已至鲫骗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踩晶,已是汗流浹背执泰。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留渡蜻,地道東北人术吝。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像茸苇,于是被迫代替她去往敵國和親排苍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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