C++是一門弱類型的語言,提供了許多復(fù)雜和靈巧類型轉(zhuǎn)換的方式。筆者之前寫的Python與Go都是強(qiáng)類型的語言,對(duì)這種弱類型的設(shè)計(jì)實(shí)在是接受無力啊~~ ( 生活所迫,工作還得寫C++啊~~)C++語言提供了四種類型轉(zhuǎn)換的操作:static_cast,dynamic_cast,reinterpret_cast,const_cast,今天就來聊一聊,在C++之中應(yīng)該如何來使用這些類型轉(zhuǎn)換的。
1.舊式類型轉(zhuǎn)換
開門見山,先聊聊筆者對(duì)類型轉(zhuǎn)換的看法吧。從設(shè)計(jì)上看,一門面向?qū)ο蟮恼Z言是不一樣提供類型轉(zhuǎn)換的,這種方式破壞了類型系統(tǒng)。C++為了兼容C也不得不吞下這個(gè)苦果,在實(shí)際進(jìn)行編程工作的過程之中,并不太推薦大家使用類型轉(zhuǎn)換。(Java在這里就做了一些妥協(xié),在基本類型之中提供了類型轉(zhuǎn)換。對(duì)于對(duì)象類型則不提供類型轉(zhuǎn)換這種黑魔法)
C++之中提供了兩種類型轉(zhuǎn)換的方式,第一種方式沿用了C語言之中的類型轉(zhuǎn)換,稱之為舊式類型轉(zhuǎn)換。說起來也很簡單,舉個(gè)栗子:
char x = 'c';
int y = (int) x;
這是最簡單的一個(gè)舊式類型轉(zhuǎn)換,一個(gè)char類型被裝換為一個(gè)int類型。但是這種舊式的類型轉(zhuǎn)換是存在問題的:過于粗暴且極易失控,所以C++新提供了四種新式的類型轉(zhuǎn)換來替代舊式的類型轉(zhuǎn)換,這四種類型轉(zhuǎn)換分別用于不用的轉(zhuǎn)換場景,接下來筆者來一一梳理一下它們的用法。
2.新式的類型轉(zhuǎn)換
C++語言提供了四種新式類型轉(zhuǎn)換的操作:
static_cast,dynamic_cast,reinterpret_cast,const_cast,這些操作都依托了C++的模板來使用,標(biāo)準(zhǔn)的用法是
xxx_cast<轉(zhuǎn)換類型>(轉(zhuǎn)換參數(shù))
這種新式轉(zhuǎn)換優(yōu)于舊式的轉(zhuǎn)換就在于:編譯器可以在轉(zhuǎn)換期間進(jìn)行更多的檢查,對(duì)于一些不符合轉(zhuǎn)換邏輯的轉(zhuǎn)換進(jìn)行一些糾錯(cuò)。而某些類型轉(zhuǎn)換操作可以利用RTTI(運(yùn)行時(shí)類型信息)來確保類型轉(zhuǎn)換的合理,這是舊式的類型轉(zhuǎn)換無法達(dá)成的效果。
- const_cast
從名字上就可以看出來,這廝是用來對(duì)const屬性進(jìn)行類型轉(zhuǎn)換的。這個(gè)名字取得有些偏頗,它同樣適用于volatile屬性。它可以為變量添加或接觸上述屬性,它也是新式轉(zhuǎn)換之中唯一具有這個(gè)能力的轉(zhuǎn)換方式,沒有什么額外的坑,用戶體驗(yàn)良好:(但是偶爾對(duì)于const屬性的轉(zhuǎn)換需要執(zhí)行多步,先通過const_cast轉(zhuǎn)換,再借助其他轉(zhuǎn)換)
//函數(shù)需要傳遞const屬性的變量,如atoi
atoi(const_cast<const char*>(char_ptr))
- static_cast
static_cast 是靜態(tài)的轉(zhuǎn)換形式,不通過運(yùn)行時(shí)類型檢查來保證轉(zhuǎn)換的安全性。它主要用于如下場合:
用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把long轉(zhuǎn)換成char,把int轉(zhuǎn)換成char。
用于面向?qū)ο缶幊讨谢惻c派生類之間的指針或引用轉(zhuǎn)換。它分為兩種
上行轉(zhuǎn)換(把派生類的指針或引用轉(zhuǎn)換成基類)是安全的;
下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成派生類),由于沒有運(yùn)行時(shí)的動(dòng)態(tài)類型檢查,所以是不安全的。把非const屬性的類型轉(zhuǎn)換為const類型,但是不能將const類型轉(zhuǎn)換為非const類型,這個(gè)得借助前文的const_cast。
void 的空指針轉(zhuǎn)換成其他類型的的空指針。
上面的幾種概念的比較好理解,這里筆者著重聊聊上下行轉(zhuǎn)換:不啰嗦,看代碼:
class Bird {
public:
virtual void fly() {
cout << "I can fly." << endl;
}
};
class Penguin:public Bird {
public:
void fly() {
cout << "I can't fly." << endl;
}
};
上述代碼我們定義了兩個(gè)類Bird與Penguin:
int main() {
Penguin* p = new (std::nothrow) Penguin;
Bird* b = static_cast<Bird *>(p);
b->fly();
return 0;
}
上行轉(zhuǎn)換,將派生類轉(zhuǎn)換為基類的指針,合法。
int main() {
Bird* b = new (std::nothrow) Bird;
Penguin* p = static_cast<Penguin *>(b);
if (p != nullptr) {
p->fly();
} else {
}
return 0;
}
下行轉(zhuǎn)換,將基類轉(zhuǎn)換為派生類的指針,此時(shí)程序的行為是不確定的。并且編譯期間并沒有警告,這是一種十分危險(xiǎn)的用法,所以使用時(shí)一定要謹(jǐn)小慎微。所以接下來就要請(qǐng)出下一種轉(zhuǎn)換dynamic_cast,這是在對(duì)象基類和派生類之間轉(zhuǎn)換推薦的一種方式。
- dynamic_cast
dynamic_cast主要用于在類層次間進(jìn)行上下行轉(zhuǎn)換時(shí),它與static_cast的最大的區(qū)別就在于dynamic_cast能夠在運(yùn)行時(shí)進(jìn)行類型檢查的功能,所以做起類型轉(zhuǎn)換比static_cast更安全,但是dynamic_cast會(huì)耗費(fèi)更多的系統(tǒng)資源。dynamic_cast是無法通過舊式類型轉(zhuǎn)換完成的類型轉(zhuǎn)換。
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對(duì)于非法的下行轉(zhuǎn)換會(huì)返回空指針,所以可以在一定程度上避免不安全的類型轉(zhuǎn)換。
-
reinterpret_cast
reinterpret_cast主要用于指針類型之間的轉(zhuǎn)換,和對(duì)象到指針類型之間的轉(zhuǎn)換。reinterpret就是對(duì)數(shù)據(jù)的比特位重新解釋轉(zhuǎn)換為我們需要轉(zhuǎn)換的對(duì)象。其與static_cast的區(qū)別在于其能處理有繼承關(guān)系類的指針和內(nèi)置數(shù)據(jù)類型的轉(zhuǎn)換。而reinterpret_cast能夠處理所有指針(引用)之間的轉(zhuǎn)換
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;
}
上述代碼依舊可以轉(zhuǎn)換成功,結(jié)果不可控。
3.小結(jié)
梳理完C++新引進(jìn)的四種類型轉(zhuǎn)換符之后,想必大家在實(shí)踐之中可以很好的運(yùn)用好這些C++的類型轉(zhuǎn)換。后續(xù)筆者還會(huì)繼續(xù)深入的探討有關(guān)C++之中類型系統(tǒng)相關(guān)的內(nèi)容,歡迎大家多多指教。