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