參考文章:
1、【C++】四種強制類型轉換
2、四種強制類型轉換
C++ 四種強制類型轉換
C語言中的強制類型轉換(Type Cast)有顯式和隱式兩種,顯式一般就是直接用小括號強制轉換,TYPE b = (TYPE)a; 隱式就是直接 float b = 0.5; int a = b; 這樣隱式截斷(by the way 這樣隱式的截斷是向 0 取整的,我喜歡這么叫因為 0.9 會變成 0,1.9 變成 1,-0.9 變成 0,-1.9 變成 -1)。
C++對C兼容,所以上述方式的類型轉換是可以的,但是有時候會有問題,所以推薦使用C++中的四個強制類型轉換的關鍵字:
1、static_cast,2、const_cast,3、reinterpret_cast,4、dynamic_cast
靜態轉換、常量轉換、重解釋、動態轉換
1)static_cast
這應該四種中是最常見的。用法為static_cast<type-id> (expression)
。
該運算符把 expression 轉換為 type-id 類型,但沒有運行時類型檢查來保證轉換的安全性。
主要用法如下:
(1)用於類層次結構中基類(父類)和派生類(子類)之間指針或引用的轉換。
進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;
進行下行轉換(把基類指針或引用轉換成派生類表示)時,由於沒有動態類型檢查,所以是不安全的。而且不推薦這種方式轉換,如果有這種需要,最好使用dynamic_cast。
(2)用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
(3)把空指針轉換成目標類型的空指針。
(4)把任何類型的表達式轉換成void類型。
最常用的應該還是基本數據類型之間的轉換,如下:
const auto a1 = 11; // int
const auto a2 = 4; // int
// C style
double res1 = (double)(a1) / (double)(a2); // 其實寫一個 (double) 就行
cout << "res1 = " << res1 << endl; // res1 = 2.75
// C++ style
auto res2 = static_cast<double>(a1) / static_cast<double>(a2);
cout << "res2 = " << res2 << endl; // res2 = 2.75
cout << typeid(res2).name() << endl; // double
當然也有對類的操作:
class A{
public:
virtual void f(){
cout<<"A"<<endl;
}
void ff(){
cout<<"AA"<<endl;
}
};
class B:public A{
public:
virtual void f(){
cout<<"B"<<endl;
}
void ff(){
cout<<"BB"<<endl;
}
};
int main()
{
/*使用static_cast將A類型指針轉換成B類型指針,向下轉換*/
A *a=new B();
a->f();
a->ff();
B *b=static_cast<B*>(a);//向下轉換,不推薦這種做法,盡量用dynamic_cast
b->f();
b->ff();
/*使用static_cast將B類型指針轉換成A類型指針,向上轉換,這種情況是安全的,就不演示了*/
}
運行結果:
[Running] cd "e:\source_vscode\demo\" && g++ demo.cpp -o demo && "e:\source_vscode\demo\"demo
B
AA
B
BB
[Done] exited with code=0 in 4.255 seconds
需要注意的是,static_cast 不能轉換掉 expression 的 const、volitale 或者 __unaligned 屬性。
2)const_cast
上邊的 static_cast 不能將 const int* 轉成 int*,const_cast 就可以,用法為``` const_cast<type-i> (expression) ```。如下面代碼:
const int a = 10;
const int * p = &a;
*p = 20; // Compile error: Cannot assign readonly type 'int const'
int res1 = const_cast<int>(a); // Compile error: Cannot cast from 'int' to 'int' via const_cast
// only conversions to reference or pointer types are allowed
int* res2 = const_cast<int*>(p); // ok
也就是說,const_cast<>里邊的內容必須是引用或者指針,就連把 int 轉成 int 都不行。
總結來說,const_cast 通常是無奈之舉,只是 C++ 提供了一種修改 const 變量的方式,但這種方式並沒有什么實質性的用處,還是不用的好。const 的變量不要讓它變。
注意:const_cast的作用並不是去修改一個const值,而是當你的值定義為一個const的時候,而你去調用函數,這個函數可能不是你自己寫的,這個函數的參數必須是是一個非const值,那么通過const_cast強制轉換未非const值,但是又不會改變const的值。
3)reinterpret_cast
reinterpret_cast 主要有三種強制轉換用途:
1、改變指針或引用的類型
2、將指針或引用轉換為一個足夠長度的整形
3、將整型轉換為指針或引用類型。
用法為reinterpret_cast <type-id> (expression)
。
type-id 必須是一個指針、引用、算術類型、函數針或者成員指針。它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還可以得到原先的指針值)。
我們映射到的類型僅僅是為了故弄玄虛和其他目的,這是所有映射中最危險的。(這句話是C++編程思想中的原話)。因此, 你需要謹慎使用 reinterpret_cast。
4)dynamic_cast
用法為dynamic_cast<type-id> (expression)
。
幾個特點如下:
(1)其他三種都是編譯時完成的,dynamic_cast 是運行時處理的,運行時要進行類型檢查。
(2)不能用於內置的基本數據類型的強制轉換
(3)dynamic_cast 要求 <> 內所描述的目標類型必須為指針或引用。dynamic_cast 轉換如果成功的話返回的是指向類的指針或引用,轉換失敗的話則會返回 nullptr
(4)在類的轉換時,在類層次間進行上行轉換(子類指針指向父類指針)時,dynamic_cast 和 static_cast 的效果是一樣的。在進行下行轉換(父類指針轉化為子類指針)時,dynamic_cast 具有類型檢查的功能,比 static_cast 更安全。 向下轉換的成功與否還與將要轉換的類型有關,即要轉換的指針指向的對象的實際類型與轉換以后的對象類型一定要相同,否則轉換失敗。在C++中,編譯期的類型轉換有可能會在運行時出現錯誤,特別是涉及到類對象的指針或引用操作時,更容易產生錯誤。Dynamic_cast操作符則可以在運行期對可能產生問題的類型轉換進行測試。
(5)使用 dynamic_cast 進行轉換的,基類中一定要有虛函數,否則編譯不通過(類中存在虛函數,就說明它有想要讓基類指針或引用指向派生類對象的情況,此時轉換才有意義)。這是由於運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表中,只有定義了虛函數的類才有虛函數表(C++中的虛函數基本原理這篇文章寫得不錯,https://blog.csdn.net/xiejingfa/article/details/50454819)。
class base {
public:
void print1() { cout << "in class base" << endl; }
};
class derived : public base {
public:
void print2() { cout << "in class derived" << endl; }
};
int main() {
derived *p, *q;
// p = new base; // Compilr Error: 無法從 "base * " 轉換為 "derived * "
// Compile Error: Cannot cast from 'base*' to 'derived*' via dynamic_cast: expression type is not polymorphic(多態的)
// p = dynamic_cast<derived *>(new base);
q = static_cast<derived*>(new base); // ok, but not recommended
q->print1(); // in class base
q->print2(); // in class derived
}