C++的轉換手段并與Explicit關鍵詞配合使用
C中我們會進行各種類型的強制轉化,而在C中我們經常可以看到這種轉換
- memset(OTA_FLAG_ADDRESS,(uint8_t*)&OTA_Flag,sizeof(OTA_Flag));
而C++的類型轉化和C差別很多,那么C++里面的類型轉化是怎么用的呢。C++除了隱式轉換和顯示轉化,顯示轉化是我們熟知,有四個顯示轉化函數:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要運用于繼承關系類間的強制轉化。
下面我就給大家說道說道。
隱式轉化
c++語言不會直接將兩個不同類型的值相加,二十先根據類型轉化規則設法將運算對象的類型統一后再求值。例如 int value = 3.14 +3;這個程序是可以編譯通過的,只不過編譯器可能會警告改運算損失了精度。這樣的類型轉化是自動運行,無需程序員介入,因此,它們被稱為隱式轉換
何時會發生隱式轉化:
在下面的情況中,編譯器會自動的轉化運算對象的類型:
- 在大多數表達式中,需要將比int類型小的整型值首先提升到較大的整數類型會進行隱式轉化
- 在條件中,非布爾值轉換成布爾類型
- 初始化過程中,初始值轉化成變量的類型;在賦值語句中,右側運算對象轉化成左側運算對象的類型
- 函數調用時候也會發生類型轉化(實參類型轉化)
顯示轉化
C風格的強制轉換(Type Cast)容易理解,不管什么類型的轉換都可以使用使用下面的方式
- Type b = (Type)a;
當然,C++也是支持C風格的強制轉換,但是C風格的強制轉換可能帶來一些隱患,讓一些問題難以察覺.所以C++提供了一組可以用在不同場合的強制轉換的函數.
C++提供了四種強制類型轉化的函數,分別是:
static_cast,命名上理解是靜態類型轉換。大部分C實現的轉化,用這個函數就可以了。
const_cast,字面上理解就是去const屬性。
dynamic_cast,命名上理解是動態類型轉換。如子類和父類之間的多態類型轉換。
reinterpret_cast,僅僅重新解釋類型,但沒有進行二進制的轉換。
static_cast:
任何具有明確定義的類型轉化,只要不包含底層const,都可以使用static_cast,舉一個例子。
- double slop = static_cast<double>(j) / i;//進行強制轉化成double
- int*pn =&n;
- double*dp = static_cast<double*>(&pn) //無關類型指針轉換,編譯錯誤
- void*p = &n;
- double*d = static_cast<double*>(&p) //將void*轉化為初始的指針類型
static_cast強制轉換只會在編譯時檢查,但沒有運行時類型檢查來保證轉換的安全性。同時,static_cast也不能去掉expression的const、volitale、或者__unaligned屬性。
const_cast:
const_cast<>可以實現將const 指針類型轉普通指針,const_cast操作不能在不同的種類間轉換。相反,它僅僅把一個它作用的表達式轉換成常量。它可以使一個本來不是const類型的數據轉換成const類型的,或者把const屬性去掉。如果一個對象本身不是一個常量,使用強制類型轉化獲得寫權限是合法的行為。然而如果對象是一個常量,再使用const_cast執行寫操作就會產生未定義的后果。
只有const_cast能改變表達式的常量屬性,使用其他形式的命名強制類型轉化改變表達式的常量屬性都將引發編譯器錯誤。同樣的,也不能用const_cast改變表達式的類型:
- const char *cp;
- char *q = static_cast<char *>(cp);//錯誤:static_cast不能轉換掉const性質
- static_cast<string>(cp);//正確:字符串字面值轉換成string類型
- const_cast<string>(cp);//錯誤:const_cast 只改變常量屬性
- const_cast<char*>(cp);//正確
- const int p = 0;
- int &rb = const_cast<int&>(p);//正確
- rb =10;
dynamic_cast:
1.其他三種都是編譯時完成的,dynamic_cast是運行時處理的,運行時要進行類型檢查。
2.不能用于內置的基本數據類型的強制轉換。
3.dynamic_cast轉換如果成功的話返回的是指向類的指針或引用,轉換失敗的話則會返回NULL。
4.使用dynamic_cast進行轉換的,基類中一定要有虛函數,否則編譯不通過。可以從父類轉基類,但是可能為空
5.在類的轉換時,在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的。在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。向上轉換即為指向子類對象的向下轉換,即將父類指針轉化子類指針。向下轉換的成功與否還與將要轉換的類型有關,即要轉換的指針指向的對象的實際類型與轉換以后的對象類型一定要相同,否則轉換失敗。
- class BaseClass {
- public:
- int Num;
- virtualvoid foo(){}; //基類必須有虛函數。保持多臺特性才能使用dynamic_cast
- };
- DerivedClass: public BaseClass {
- public:
- char*m_szName[100];
- void bar(){};
- };
- int main(int argc,char** argv)
- {
- BaseClass* pb =new DerivedClass();
- DerivedClass *pd1 = static_cast<DerivedClass *>(pb); //子類->父類,靜態類型轉換,正確但不推薦
- DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb); //子類->父類,動態類型轉換,正確
- BaseClass* pb2 =new BaseClass();
- DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); //父類->子類,靜態類型轉換,危險!訪問子類m_szName成員越界
- DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); //父類->子類,動態類型轉換,安全的。結果是NULL
reinterpret_cast:
實現指針轉整形,整形轉指針.reinterpret_cast是強制類型轉換符用來處理無關類型轉換的,通常為操作數的位模式提供較低層次的重新解釋!但是它僅僅是重新解釋了給出的對象的比特模型,并沒有進行二進制的轉換!
它是用在任意的指針之間的轉換,引用之間的轉換,指針和足夠大的int型之間的轉換,整數到指針的轉換。最普通的用途就是在函數指針類型之間進行轉換。
請看一個簡單代碼
- int doSomething(){return 0;};
- typedef void(*FuncPtr)(); //FuncPtr is 一個指向函數的指針,該函數沒有參數,返回值類型為 void
- FuncPtr funcPtrArray[10]; //10個FuncPtrs指針的數組 讓我們假設你希望(因為某些莫名其妙的原因)把一個指向下面函數的指針存入funcPtrArray數組:
- funcPtrArray[0] =&doSomething;// 編譯錯誤!類型不匹配,reinterpret_cast可以讓編譯器以你的方法去看待它們:funcPtrArray
- funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); //不同函數指針類型之間進行轉換
explicit關鍵字(顯示的類型轉化運算符)
C++提供了關鍵字explicit,可以阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。即聲明為explicit的構造函數不能在隱式轉換中使用。
explicit關鍵字只能用于類內部的構造函數聲明上,而不能用在類外部的函數定義上。現在Things類像這樣:
- class Things
- {
- public:
- Things(const std::string&name ="");
- explicit operator int() const{return val;}
- //編譯器不會自動執行這一類型的
- };
下面再看個好一點的例子進行對比一下:
- // 類的通過構造函數的隱式轉換:
- #include <iostream>
- using namespace std;
- class A {};
- class B {
- public:
- // conversion from A (constructor):
- B (const A& x) {}
- // conversion from A (assignment):
- B& operator= (const A& x) {return *this;}
- // conversion to A (type-cast operator)
- operator A() {return A();}
- };
- int main ()
- {
- A foo;
- B bar = foo; // 調用構造函數實現隱式類型轉換
- bar = foo; // calls assignment
- foo = bar; // calls type-cast operator,相當于 foo = A(bar);
- return 0;
- }
再看使用explicit的一個例子:
- #include <iostream>
- using namespace std;
- class A {};
- class B {
- public:
- explicit B (const A& x) {}
- B& operator= (const A& x) {return *this;}
- operator A() {return A();}
- };
- void fn (B x) {} // 當我們希望x只能是B類型時,我們就需要禁止隱式類型轉換
- int main ()
- {
- A foo;
- B bar (foo); // 必須顯式類型轉換,不再允許B bar = foo;
- bar = foo;
- foo = bar;
- // fn (foo); // 不允許隱式類型轉換
- fn (bar);
- return 0;
- }
這就是我分享的c++的轉化類型方法,其中參考了好多人的文字,此外如果大家有什么更好的思路,也歡迎分享交流哈。
本文轉載自微信公眾號「羽林君」,可以通過以下二維碼關注。轉載本文請聯系羽林君公眾號。