1.C語言中的類型轉換
在C語言中,如果賦值運算符左右兩側類型不同,或者形參與實參類型不匹配,或者返回值類型與接收返回值類型不一致時,就需要發生類型轉化,C語言中總共有兩種形式的類型轉換:隱式類型轉換和顯式類型轉換。
1.隱式類型轉化:編譯器在編譯階段自動進行,能轉就轉,不能轉就編譯失敗
2.顯式類型轉化:需要用戶自己處理
void Test()
{int i = 1;// 隱式類型轉換double d = i;printf("%d, %.2f\n", i, d);int* p = &i;// 顯示的強制類型轉換int address = (int)p;printf("%x, %d\n", p, address);
}
缺陷:
轉換的可視性比較差,所有的轉換形式都是以一種相同形式書寫,難以跟蹤錯誤的轉換
2.為什么C++需要四種類型轉換
C風格的轉換格式很簡單,但是有不少缺點的:
1.隱式類型轉化有些情況下可能會出問題:比如數據精度丟失
2.顯式類型轉換將所有情況混合在一起,代碼不夠清晰
因此C++提出了自己的類型轉化風格,注意因為C++要兼容C語言,所以C++中還可以使用C語言的轉化風格。
3.C++強制類型轉換
標準C++為了加強類型轉換的可視性,引入了四種命名的強制類型轉換操作符:
1.static_cast
2.reinterpret_cast
3.const_cast
4.dynamic_cast
3.1 static_cast
static_cast用于非多態類型的轉換(靜態轉換),編譯器隱式執行的任何類型轉換都可用static_cast,但它不能用于兩個不相關的類型進行轉換
int main()
{double d = 12.34;int a = static_cast<int> (d);cout << a << endl;return 0;
}
3.2 reinterpret_cast
reinterpret_cast操作符通常為操作數的位模式提供較低層次的重新解釋,reinterpret_cast 用于進行任意類型之間的轉換,包括無關類型之間的轉換。reinterpret_cast提供了一種底層的轉換方式,允許將一個指針或引用轉換為一個完全不相關的類型。它在類型之間的轉換上沒有限制,但是無法提供任何類型安全保證,使用時需要特別小心。
int main()
{double d = 12.34;int a = static_cast<int>(d);cout << a << endl;// 這里使用static_cast會報錯,應該使用reinterpret_cast//int *p = static_cast<int*>(a);int* p = reinterpret_cast<int*>(a);return 0;
}
3.3 const_cast
const_cast: 用于去除指針或引用的const屬性。const_cast可以修改指針或引用的底層const屬性,但是不能修改常量對象本身的值。它主要用于將const對象轉換為非const對象,以便可以修改其值。
int main()
{//在C++中,volatile是一個關鍵字,用于修飾變量,用來指示編譯器不要進行某些優化,以確//保對該變量的操作在程序執行期間的可見性和有序性。//通常情況下,編譯器會對變量進行各種優化,例如將變量保存在寄存器中以提高訪問//速度,重排代碼以優化執行流程等。然而,對于被volatile修飾的變量,編譯器會在編譯//和優化過程中對其保持謹慎,并生成對其操作的指令,以確保在程序執行期間的可見性和有序性。volatile const int a = 2;int* p = const_cast<int*>(&a);// &a的類型是 const int**p = 3;cout << a << endl;//如果不添加volatile則輸出2,因為編譯器的優化直接把a替換成了2return 0;
}
3.4 dynamic_cast
dynamic_cast用于將一個父類對象的指針/引用轉換為子類對象的指針或引用(動態轉換)
向上轉型:子類對象指針/引用->父類指針/引用(不需要轉換,賦值兼容規則)
向下轉型:父類對象指針/引用->子類指針/引用(用dynamic_cast轉型是安全的)
注意:
1.dynamic_cast只能用于父類含有虛函數的類
2.dynamic_cast會先檢查是否能轉換成功,能成功則轉換,不能則返回0
class A
{
public:virtual void f(){}
};class B : public A
{};void fun(A* pa, const string& s)
{cout <<"pa指向"<<s << endl;// dynamic_cast會先檢查是否能轉換成功,能成功則轉換,不能則返回B* pb1 = (B*)pa;// 不安全的,因為如果B有自己的成員,那么用指針可以訪問這些成員,但是這個訪問就強制越界了B* pb2 = dynamic_cast<B*>(pa); // 安全的cout << "[強制轉換]pb1:" << pb1 << endl;cout << "[dynamic_cast轉換]pb2:" << pb2 << endl << endl;
}int main()
{A a;B b;fun(&a, "指向父類對象");fun(&b, "指向子類對象");return 0;
}
注意:
強制類型轉換關閉或掛起了正常的類型檢查,每次使用強制類型轉換前,程序員應該仔細考慮是否還有其他不同的方法達到同一目的,如果非強制類型轉換不可,則應限制強制轉換值的作用域,以減少發生錯誤的機會。強烈建議:避免使用強制類型轉換
4.RTTI
RTTI:Run-time Type identification的簡稱,即:運行時類型識別。
C++通過以下方式來支持RTTI:
1. typeid運算符
typeid是C++的一個運算符,用于獲取表達式的類型信息。它的語法形式是typeid(expression),其中expression可以是任何表達式、變量、類型或者類的對象。
typeid運算符返回一個type_info對象,該對象包含有關給定表達式的類型信息。type_info類提供了一些成員函數和操作符,可以用于比較類型信息、獲取類型的名稱等。
typeid運算符的主要用途包括:
- 比較類型:可以使用typeid運算符比較兩個類型信息是否相同。如果兩個類型相同(或者是相同類型的派生類),則返回true;否則返回false。示例:
class Base { ... };
class Derived : public Base { ... };Base* base = new Derived();if (typeid(*base) == typeid(Derived)) {// base指向的對象的實際類型是Derived
}
- 獲取類型名稱:可以使用type_info對象的name()成員函數獲取類型的名稱,返回一個以NULL結尾的字符串。示例:
class MyClass { ... };MyClass obj;
const std::type_info& type = typeid(obj);
std::cout << type.name() << std::endl; // 輸出類型的名稱
需要注意的是,typeid運算符只能用于在運行時獲取類型信息,而無法獲取模板參數的具體類型。此外,對于沒有多態性的類型或者不完整類型,typeid運算符可能無法正常工作。
另外,為了使用typeid運算符,需要包含頭文件<typeinfo>。
2. dynamic_cast運算符
3. decltype
decltype是C++11引入的一個關鍵字,用于獲取表達式的類型。
decltype的作用包括以下幾個方面:
- 推斷表達式的類型:可以使用decltype關鍵字推斷表達式的類型,并將其作為變量的類型或函數返回類型。這在使用模板編程、泛型編程等場景下非常有用,可以根據表達式的結果類型來進行類型推斷。
- 保留表達式的修飾符:decltype關鍵字會保留表達式的修飾符,包括const、&和&&等,確保推斷出來的類型與原表達式的類型是一致的。
- 支持表達式推斷:decltype關鍵字支持對函數調用、類成員訪問和運算符等復雜表達式進行類型推斷。
- 不進行實際計算:decltype關鍵字只進行編譯期的類型推斷,不會對表達式進行實際計算,避免了執行時的開銷。
示例:
int a = 10;
const int& b = a;
decltype(a) c = a; // 推斷c的類型為int
decltype(b) d = b; // 推斷d的類型為const int&
decltype(a + b) e = a + b; // 推斷e的類型為inttemplate<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) { // 推斷函數返回值類型為t + u的類型return t + u;
}
需要注意的是,decltype關鍵字在推斷過程中只使用表達式的類型信息,不會執行任何表達式中的實際計算。此外,如果表達式為變量,而非標識符,則decltype將推斷出變量的引用類型。
5. 常見面試題
1. C++中的4中類型轉化分別是:_________、_________、_________、_________
2. 說說4中類型轉化的應用場景。