目錄
一、內置 轉 內置
二、內置 轉 自定義
三、自定義 轉 內置
四、自定義 轉 自定義
五、類型轉換規范化
1.static_case
2.reinterpret_cast
3.const_cast
4.dynamic_cast
六、RTTI
一、內置 轉 內置
????????C++兼容C語言,在內置類型之間轉換規則和C語言一樣的,C/C++語言因為允許類型轉換所以不是類型安全的語言。
隱式轉化:編譯器識別出類型不同后,能轉就轉,不能轉則報錯。
例如:
- int = 'a'?
- char = 4
- int = 8.26
除此之外函數調用中形參和實參的類型不同時,編譯器會嘗試進行隱式轉化。
顯示轉化(強制類型轉化):用戶進行指定的顯示轉換。
例如:
- int = (int)'a'?
- char = (char)4
- int = (int)8.26
什么時候能轉什么時候不能轉呢?
????????通常有關聯性的類型,有轉換意義的類型都是可以轉化的。比如int類型與double類型,float類型,short類型等,都是描述數字的大小,又或者各種指針類型之間。
? ? ? ? 又比如int與指針不能隱式轉換,因為轉化后也是無意義的。但可以顯示轉化(強制類型轉化)。
二、內置 轉 自定義
????????c++中支持內置類型轉自定義類型,只需要提供相應的構造函數,就可以想怎么轉就怎么轉,全在于你的構造函數怎么實現。如下:
using namespace std;
class A
{
public:A(int x):a(x),b(x){}A(int x,int y,int z):a(x), b(y+z){}
private:int a;int b;
};
void fun(A x)
{//......
}
int main()
{int v = 10;A a1 = v;//int類型隱式轉化為A類型A a2 = { 1,2,3 };fun(6);return 0;
}
- A a1 = v:調用構造函數產生臨時對象,然后調用拷貝賦值。邏輯上是這樣,但實際上會被編譯器優化。
- A a2 = {1,2,3}:通過花括號傳入多個參數構造臨時對象,然后調用拷貝賦值。
- ?fun(6):在函數傳參時,同樣會隱式轉換,不用單獨構造出類型后傳入,這樣就顯得方便得多。
在構造函數的函數名前加關鍵字explicit:不被允許隱式轉換,要進行轉換需要顯示的進行。
三、自定義 轉 內置
????????c++中支持自定義類型轉內置類型,提供相應的運算符重載函數(operator),同樣可以想怎么轉就怎么轉,都是自己設定的。
????????強制類型轉化的運算符是(),按理來說應該重載()運算符,但是又與仿函數沖突,所以c++制定了特殊的函數來做類型轉化,即:
- operator 類型 ()
該函數不用寫返回類型,但在函數名后面需要加目標類型,函數結束也需要return返回。
class A
{
public:A(int x,int y):_a(x),_b(y){}operator int(){return _a + _b;}
private:int _a;int _b;
};
int main()
{A a1 = { 1,3 };int x = a1;return 0;
}
同樣可以使用explicit修飾,修飾后不支持隱式轉化。
????????比如在c++智能指針shared_ptr中使用了operator bool()把指針轉換為bool類型來判斷指針是否為空,如下:
測試如下:
#include <iostream>
#include <memory>
using namespace std;
int main()
{shared_ptr<int> p(new int(10));if (!p)//隱式轉換為bool類型cout << "p為nullptr";else cout << "p不為nullptr";return 0;
}
四、自定義 轉 自定義
????????c++中自定義與自定義之間同樣可以轉換,核心是讓構造函數產生臨時對象。比如我們要讓B類型轉換為A類型,我們可以構造這樣一個函數:
class A
{
public:A(int x):_a(x),_b(x){}int get_a(){return _a;}
private:int _a;int _b;
};
class B
{
public:B(A x):_val(x.get_a()){}
private:int _val;
};
int main()
{A a = 4;B b = a;return 0;
}
五、類型轉換規范化
????????C 風格的類型轉換過于“暴力”,允許許多不安全的轉換。C++提供的?顯式類型轉換運算符來替代傳統的 C 風格強制類型轉換(如?(int)x
)。這種機制的目的是提高代碼的?類型安全性、可讀性?和?維護性,同時限制不安全的隱式轉換。
它有四種類型轉換運算符,接下來我們依次來學習。
1.static_case
????????用于意義相近的轉換,比如int與double,char與int,左值與右值,如下:
int main()
{char ch = 'm';int x = static_cast<int>(ch);return 0;
}
2.reinterpret_cast
????????reinterpret_cast用于高風險的類型轉換,即意義不相近的類型之間,或各種指針之間的轉換,比如int與int*,char*與int*。
int main()
{int x = 10;int* p = reinterpret_cast<int*>(x);return 0;
}
3.const_cast
????????const_cast用來添加/移除const屬性的類型間轉換,或者添加/移除volatile屬性的類型間轉換,如下:
int main()
{volatile const int n = 10;int* v = const_cast<int*>(&n);//移除const屬性const int& m = const_cast<const int&>(m);//移除volatile屬性return 0;
}
4.dynamic_cast
用于基類與派生類之間的類型轉換。
????????派生類轉基類也叫切片,天然就可以實現的,但是基類轉派生類可能會存在越界訪問的問題。用這個關鍵字可以有效避免。
class Base {
public:virtual ~Base() {}
};
class Derived : public Base {};Base* base_ptr = new Base(); // 基類指針指向基類對象// 嘗試向下轉型
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if (derived_ptr == nullptr) { // 轉換失敗!
}
六、RTTI
????????RTTI(Runtime Type Information,運行時類型信息)?是 C++ 提供的一種在程序運行時獲取對象類型信息的機制。它允許程序在運行時檢測對象的實際類型,并支持安全的類型轉換(如?dynamic_cast
)和類型查詢(如?typeid
)。
????????注意:typeid并不是每次都是運行時類型識別。RTTI依賴多態類型,只有類有虛函數時才能體現?。