C++超詳細講解強制類型轉換的用法
static_cast
static_cast<type-id>(expression)
將 expression
轉換為 type-id
類型。static_cast
是靜態(tài)類型轉換,發(fā)生在編譯期。這種轉換不會進行運行時的動態(tài)檢查(RTTI),因而這種轉換可能是不安全的。static_cast
典型應用場景如下:
1. 類的層級結構中,基類和子類之間指針或者引用的轉換。
上行轉換(Upcasting),也即子類像基類方向轉換,是安全的。
下行轉換(Downcasting),也即基類向子類方向轉換,是不安全的,需要程序員自行保證安全轉換。
下面舉例說明:
class A { public: virtual void func() { std::cout << "A::func()" << std::endl; } }; class B : public A { public: virtual void func() { std::cout << "B::func()" << std::endl; } void print() { std::cout << "B::print()" << std::endl; } };
對于上行轉換,肯定是安全的。
B* pb = new B(); A* pa = static_cast<A*>(pa); pa->func();
對于下行轉換:
A* pa = new B(); B* pb = static_cast<B*>(pa); pb->print();
這里,A* pa = new B();
,由于 C++ 的多態(tài)的支持,可以使用基類指針指向子類。這里的轉換是安全的,因為 pa
初始化就指向的就是 B
。而下面的轉換則是不安全的:
A* pa = new A(); B* pb = static_cast<B*>(pa); pb->print();
此外,對于兩個不存在繼承關系的兩個類之間轉換,總是失敗的,編譯器報錯:
#include <iostream> class A { virtual void func(){} }; class B { virtual void func(){} }; int main(){ A* pa = new A(); B* pb = static_cast<B*>(pa); return 0; }
2. 基本數(shù)據類型間的轉換。這種轉換也是不安全的,需要程序員自行保證安全轉換。 例如 int
轉 short
,直接高位截斷;而 short
轉 int
則高位根據符號位填充。兩種不同類型指針間相互轉換是相當危險的,例如 int*
轉 float*
。將 int 轉換為指針類型也是危險的轉換,例如 float* p = static_cast<float*>(0X2edf);
3. 將 void
類型轉換為其他類型的指針。 顯然這種轉換也是不安全的,需要程序員自行保證安全轉換。
4. 把其他類型轉換為 void
類型。
有轉換構造函數(shù)或者類型轉換函數(shù)的類與其它類型之間的轉換。例如:
#include <iostream> class Point{ public: Point(double x, double y): m_x(x), m_y(y){ } Point(double& x): m_x(x), m_y(1.1){ } public: operator double() const { return m_x; } //類型轉換函數(shù) void print() { std::cout << "m_x: " << m_x << " m_y: " << m_y << std::endl; } private: double m_x; double m_y; }; int main() { Point p1(12.5, 23.8); double x= static_cast<double>(p1); // std::cout << x << std::endl; Point p2 = static_cast<Point>(x); // p2.print(); return 0; }
dynamic_cast
dynamic_cast<type-id>(expression)
把 expression
轉換為 type-id
類型,type-id
必須是類的指針、類的引用或者是 void *
;如果 type-id
是指針類型,那么 expression
也必須是一個指針;如果 type-id
是一個引用,那么 expression
也必須是一個引用。
dynamic_cast
提供了運行時的檢查。對于指針類型,在運行時會檢查 expression
是否真正的指向一個 type-id
類型的對象,如果是,則能進行正確的轉換;否則返回 nullptr
。對于引用類型,若是無效轉換,則在運行時會拋出異常 std::bad_cast
。
T1 obj; T2* pObj = dynamic_cast<T2*>(&obj); // 無效轉換返回 nullptr T2& refObj = dynamic_cast<T2&>(obj); // 無效轉換拋出 bad_cast 異常
上行轉換:其實和 static_cast 是一樣的,一般肯定能成功。例如前面用到的例子:
// A->B B* pb = new B(); A* pa = static_cast<A*>(pa);
但是,下面這種繼承關系會轉換失?。?/p>
#include <iostream> /* A / \ V V B C \/ v D */ class A { virtual void func(){} }; class B : public A { void func(){} }; class C : public A { void func(){} }; class D : public B, public C { void func(){} }; int main(){ D* pd = new D(); A* pa = dynamic_cast<A*>(pd); return 0; }
上面這個例子,雖然也是上行轉換,但是存在兩條路徑,在 B 和 C 都繼承于 A,并且有虛函數(shù)實現(xiàn),上行轉換不知道從哪條路徑進行轉換。下面的寫法則沒問題:
D* pd = new D(); B* pb = dynamic_cast<B*>(pd); A* pa = dynamic_cast<A*>(pb);
下行轉換:看個例子。
#include <iostream> class A { virtual void func(){} }; class B : public A { void func(){} }; int main(){ A* pa1 = new B(); A* pa2 = new A(); B *pb1 = dynamic_cast<B*>(pa1); // ok B *pb2 = dynamic_cast<B*>(pa2); // pb2 is a nullptr! return 0; }
其實 dynamic_cast
本質只支持上行轉換,只會沿著繼承鏈向上遍歷,找到目標類型則轉換成功,否則失敗。dynamic_cast
看似支持下行轉換,這都是多態(tài)的緣故。上面的例子,pa1
雖然類型是 A
,但實際指向 B
,沿著 B
向上可以找到 B
,因為第一個轉換可以成功。而 pa2
指向 A
,沿著 A
向上找不到 B
類型,因而轉換失敗。
因而在有繼承關系的類的轉換時候, static_cast
轉換總是成功的, dynamic_cast
顯然比 static_cast
更加安全。
const_cast
const_cast
用來去掉表達式的 const
修飾或 volatile
修飾,也就是將 const
或 volatile
類型轉換為非 const
或 非 volatile
類型。
#include <iostream> int main(){ const int n = 111; int *p = const_cast<int*>(&n); *p = 222; std::cout<< "n = " << n << std::endl; std::cout<< "*p = " << *p << std::endl; return 0; }
這里需要注意:按照正常理解,n 的打印值應該是 222。但是,由于編譯器的常量傳播優(yōu)化,std::cout<< "n = " << n << std::endl;
會被編譯器替換成類似 std::cout<< "n = " << 111 << std::endl;
的語義。
reinterpret_cast
reinterpret_cast
轉換直接對二進制位按照目標類型重新解釋,非常粗暴,所以風險很高,慎重使用。
#include <iostream> int main(){ char str[]="hello world!"; float *p = reinterpret_cast<float*>(str); std::cout << *p << std::endl; // 1.14314e+27 return 0; }
到此這篇關于C++超詳細講解強制類型轉換的用法的文章就介紹到這了,更多相關C++強制類型轉換內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!