1、上一遍講述了C語言的隱式類型轉換和顯示類型轉換,C語言之所以增加強制類型轉換,就是為了強調轉換的風險性,但這種強調風險的方式是比較粗放了,粒度比較大,它並沒有表明存在什么風險,風險程度如何。
2、為了使潛在風險更加細化,使問題追溯更加方便,使書寫格式更加規范,C++ 對類型轉換進行了分類,並新增了四個關鍵字來予以支持,它們分別是:
關鍵字 | 說明 |
---|---|
static_cast | 用於良性轉換,一般不會導致意外發生,風險很低。 |
const_cast | 用於 const 與非 const、volatile 與非 volatile 之間的轉換。 |
reinterpret_cast | 高度危險的轉換,這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,但是可以實現最靈活的 C++ 類型轉換。 |
dynamic_cast | 借助 RTTI,用於類型安全的向下轉型(Downcasting)。 |
這四個關鍵字的語法格式都是一樣的,具體為:
xxx_cast<newType>(data)
newType 是要轉換成的新類型,data 是被轉換的數據。例如:
老式的C風格的 double 轉 int 的寫法為:
double scores = 95.5;
int n = (int)scores;
C++ 新風格的寫法為:
double scores = 95.5;
int n = static_cast<int>(scores);
3、static_cast:
一、只能用於良性轉換,這樣的轉換風險較低,一般不會發生什么意外,例如:
- 原有的自動類型轉換,例如 short 轉 int、int 轉 double、向上轉型等;
- void 指針和具體類型指針之間的轉換,例如
void *
轉int *
、char *
轉void *
等; - 有轉換構造函數或者類型轉換函數的類與其它類型之間的轉換。
二、需要注意的是,static_cast 不能用於無關類型之間的轉換,因為這些轉換都是有風險的,例如:
- 兩個具體類型指針之間的轉換,例如
int *
轉double *
、Student *
轉int *
等。 - int 和指針之間的轉換。將一個具體的地址賦值給指針變量是非常危險的,因為該地址上的內存可能沒有分配,也可能沒有讀寫權限,恰好是可用內存反而是小概率事件。
三、static_cast 也不能用來去掉表達式的 const 修飾和 volatile 修飾。換句話說,不能將 const/volatile 類型轉換為非 const/volatile 類型。
四、static_cast 是“靜態轉換”的意思,也就是在編譯期間轉換,轉換失敗的話會拋出一個編譯錯誤。
下面的代碼演示了 static_cast 的正確用法和錯誤用法:
class Complex
{
public:
Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:
operator double() const { return m_real; } //類型轉換函數
private:
double m_real;
double m_imag;
};
int main()
{
//下面是正確的用法
int m = 100;
Complex c(12.5, 23.8);
long n = static_cast<long>(m); //寬轉換,沒有信息丟失
char ch = static_cast<char>(m); //窄轉換,可能會丟失信息
int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) ); //將void指針轉換為具體類型指針
void *p2 = static_cast<void*>(p1); //將具體類型指針,轉換為void指針
double real= static_cast<double>(c); //調用類型轉換函數
//下面的用法是錯誤的
float *p3 = static_cast<float*>(p1); //不能在兩個具體類型的指針之間進行轉換
p3 = static_cast<float*>(0X2DF9); //不能將整數轉換為指針類型
return 0;
}
4、const_cast:
const_cast 比較好理解,它用來去掉表達式的 const 修飾或 volatile 修飾。換句話說,const_cast 就是用來將 const/volatile 類型轉換為非 const/volatile 類型。
下面的代碼說明 const_cast 的用法:
int main()
{
const int n = 100;
int *p = const_cast<int*>(&n);
*p = 234;
cout<<"n = "<<n<<endl; //n=100
cout<<"*p = "<<*p<<endl; //*p=234;
return 0;
}
5、reinterpret_cast:
reinterpret 是“重新解釋”的意思,顧名思義,reinterpret_cast 這種轉換僅僅是對二進制位的重新解釋,不會借助已有的轉換規則對數據進行調整,非常簡單粗暴,所以風險很高。
reinterpret_cast 可以認為是 static_cast 的一種補充,一些 static_cast 不能完成的轉換,就可以用 reinterpret_cast 來完成,例如兩個具體類型指針之間的轉換、int 和指針之間的轉換(有些編譯器只允許 int 轉指針,不允許反過來)。
下面的代碼代碼說明 reinterpret_cast 的使用:
class A
{
public:
A(int a = 0, int b = 0): m_a(a), m_b(b){}
private:
int m_a;
int m_b;
};
int main()
{
char str[]="http://c.biancheng.net"; //將 char* 轉換為 float*
float *p1 = reinterpret_cast<float*>(str);
cout<<*p1<<endl;
int *p = reinterpret_cast<int*>(100);//將 int 轉換為 int*
p = reinterpret_cast<int*>(new A(25, 96));//將 A* 轉換為 int*
cout<<*p<<endl;
return 0;
}
運行結果:
3.0262e+29
25
可以想象,用一個 float 指針來操作一個 char 數組是一件多么荒誕和危險的事情,這樣的轉換方式不到萬不得已的時候不要使用。將A*
轉換為int*
,使用指針直接訪問 private 成員刺穿了一個類的封裝性,更好的辦法是讓類提供 get/set 函數,間接地訪問成員變量。
6、dynamic_cast:
dynamic_cast 用於在類的繼承層次之間進行類型轉換,它既允許向上轉型(Upcasting),也允許向下轉型(Downcasting)。向上轉型是無條件的,不會進行任何檢測,所以都能成功;向下轉型的前提必須是安全的,要借助 RTTI 進行檢測,所有只有一部分能成功。
dynamic_cast 與 static_cast 是相對的,dynamic_cast 是“動態轉換”的意思,static_cast 是“靜態轉換”的意思。dynamic_cast 會在程序運行期間借助 RTTI 進行類型轉換,這就要求基類必須包含虛函數;static_cast 在編譯期間完成類型轉換,能夠更加及時地發現錯誤。
dynamic_cast 的語法格式為:
dynamic_cast <newType> (expression)
newType 和 expression 必須同時是指針類型或者引用類型。換句話說,dynamic_cast 只能轉換指針類型和引用類型,其它類型(int、double、數組、類、結構體等)都不行。
對於指針,如果轉換失敗將返回 NULL;對於引用,如果轉換失敗將拋出std::bad_cast
異常。