C/C++類型轉換
1. C類型轉換
C 語言中的類型轉換主要分為兩種:
-
隱式類型轉換 (Implicit Conversion)?- 由編譯器自動完成。
-
顯式類型轉換 (Explicit Conversion)?- 由程序員強制指定,也稱為強制類型轉換。
1.2 隱式類型轉換
編譯器在編譯時自動進行的轉換,通常發生在不同數據類型的變量混合運算、賦值或函數調用時。
轉換規則(通常遵循向上轉換原則):
int -> unsigned int -> long -> unsigned long -> long long -> unsigned long long -> float -> double -> long double
常見發生的場景
int i = 10;
float f = 3.14;
double d = i + f; // i 被自動轉換為 float 參與運算,結果再轉換為 double 賦值給 d
1.3 強制類型轉換
當用戶需要明確地將一種數據類型轉換為另一種時使用。它使用強制類型轉換運算符,語法是在要轉換的目標類型前加上括號,然后放在值或表達式前面。
常見發生的場景
int a = 5, b = 2;
float result;
result = a / b; // 錯誤:結果是 2.0 (整數除法)
result = (float)a / b; // 正確:結果是 2.5。將 a 轉為 float,b 也會被隱式轉換為 float
將 void 指針轉換為具體類型指針:
void* generic_ptr;
int* int_ptr;
int x = 10;generic_ptr = &x; // 合法:任何指針都可以賦值給 void*
// *generic_ptr; // 錯誤:不能對 void* 解引用
int_ptr = (int*)generic_ptr; // 必須強制轉換回 int*
printf("%d\n", *int_ptr); // 輸出 10
注意:?在 C 語言中,從?
void*
?轉換到其他指針類型必須使用強制轉換,而在 C++ 中?static_cast
?可以完成這個工作,并且從?void*
?到其他指針類型的隱式轉換在 C++ 中是非法的。
2. C++中的類型轉換
2.1 隱式類型轉換
-
C++支持內置類型向自定義類型之間的轉換,內置類型轉換為自定義類型需要構造函數的支持。
-
C++支持自定義類型轉換為內置類型,通過運算符重載
operator type ()
的函數支持。 -
C++支持自定義類型向自定義類型之間的轉換,需要對應類型的構造函數支持。
2.1.1 內置類型 -> 自定義類型(通過構造函數)
#include <iostream>class Meter {
private:double value;
public:// 關鍵:接收單一參數的構造函數// 定義了從 double -> Meter 的轉換規則Meter(double val) : value(val) {std::cout << "構造函數被調用,將 double " << val << " 轉換為 Meter" << std::endl;}
};int main() {Meter m1 = 5.7; // 隱式轉換:double -> Meterreturn 0;
}
注意:?使用?
explicit
?關鍵字可以禁止隱式轉換,只允許顯式轉換。
2.1.2 自定義類型 -> 內置類型(通過轉換函數)
通過在類中定義?operator type()
?成員函數,可以實現從自定義類型到內置類型的轉換。
#include <iostream>class Meter {
private:double value;
public:Meter(double val) : value(val) {}// 轉換函數:Meter -> doubleoperator double() const {std::cout << "轉換函數被調用,將 Meter 轉換為 double: " << value << std::endl;return value;}
};int main() {Meter m(5.7);// 自定義類型 -> 內置類型double length_in_double = m; // 隱式調用 operator double()std::cout << "轉換為 double 的值: " << length_in_double << std::endl;return 0;
}
通過operator bool()函數,可以將自定義類型當作判斷條件。
2.1.3 自定義類型 -> 自定義類型(通過構造函數或轉換函數)
#include <iostream>class Kilometer; // 前向聲明class Meter {
private:double value;
public:Meter(double val) : value(val) {}double getValue() const { return value; }// 也可以定義到 Kilometer 的轉換函數// operator Kilometer() const;
};class Kilometer {
private:double value;
public:Kilometer(double val) : value(val) {}// 關鍵:定義接收 Meter 參數的構造函數// 提供了 Meter -> Kilometer 的轉換路徑Kilometer(const Meter& m) : value(m.getValue() / 1000.0) {std::cout << "Kilometer 構造函數:將 Meter 轉換為 Kilometer" << std::endl;}void display() const {std::cout << value << " kilometers" << std::endl;}
};// Meter 中轉換函數的實現
// Meter::operator Kilometer() const {
// return Kilometer(value / 1000.0);
// }int main() {Meter m(1500.0); // 自定義類型 -> 自定義類型Kilometer km = m; // 隱式轉換:Meter -> Kilometerkm.display(); // 輸出: 1.5 kilometersreturn 0;
}
對于轉換函數,也可以使用?explicit
?關鍵字來禁止隱式轉換:
explicit operator double() const {return value;
}Meter m(5.7);
double d1 = m; // 錯誤:不能隱式轉換
double d2 = static_cast<double>(m); // 正確:顯式轉換
2.2 顯示類型轉換
2.2.1 static_cast
靜態轉換:最常用的顯式轉換,用于在編譯期進行有明確關聯的安全轉換。
場景:
-
基本數據類型之間的轉換
-
void 指針與具體類型指針之間的轉換
-
類層次結構中的上行轉換(派生類→基類)
int i = 10;
double d = static_cast<double>(i); // int -> doublevoid* voidPtr = &i;
int* intPtr = static_cast<int*>(voidPtr); // void* -> int*
2.2.2 reinterpret_cast
reinterpret_cast
?用于在兩種不相關類型之間進行轉換,其本質是對原始數據的底層位模式進行重新解釋,也就是說轉換后對原有內存的訪問解釋已經完全改變了。
場景:
-
任意指針類型之間的轉換
-
指針和整數之間的轉換
int i = 0x12345678;
char* p1 = reinterpret_cast<char*>(a);
2.2.3 const_cast
const_cast?于const類型到?const類型的轉換,去掉了const屬性。
void oldFunction(char* str) {cout << str << endl;
}const char* message = "Hello";// oldFunction(message); // 錯誤:不能將 const char* 轉換為 char*
oldFunction(const_cast<char*>(message)); // 正確// 危險示例!
const int ci = 10;
int* badPtr = const_cast<int*>(&ci);
*badPtr = 20; // 未定義行為!
2.2.4 dynamic_cast
-
dynamic_cast?于將基類的指針或者引?安全的轉換成派?類的指針或者引?。如果基類的指針或者引?時指向派?類對象的,則轉換回派?類指針或者引?時可以成功的,如果基類的指針指向基類對象,則轉換失敗返回nullptr,如果基類引?指向基類對象,則轉換失敗,拋出bad_cast異常。
-
其次dynamic_cast要求基類必須是多態類型,也就是基類中必須有虛函數。因為dynamic_cast是運?時通過虛表中存儲的type_info判斷基類指針指向的是基類對象還是派?類對象。
場景:
- 安全的下行轉換(基類→派生類)
要求:
- 基類必須有虛函數(多態類型)
class Base { virtual void foo() {} };
class Derived : public Base {};Base* basePtr1 = new Derived(); // 實際指向 Derived
Base* basePtr2 = new Base(); // 實際指向 Base// 安全的下行轉換
Derived* d1 = dynamic_cast<Derived*>(basePtr1); // 成功
Derived* d2 = dynamic_cast<Derived*>(basePtr2); // 失敗,返回 nullptrif (d2) {// 安全操作
} else {cout << "轉換失敗!" << endl;
}// 引用轉換(失敗會拋出異常)
try {Derived& rd = dynamic_cast<Derived&>(*basePtr2);
} catch (const std::bad_cast& e) {cout << "引用轉換失敗" << endl;
}
-
dynamic_cast
?的實現基礎是?RTTI(運行時類型信息)。編譯器會為每個包含虛函數的類生成額外的類型信息,這些信息在程序運行時可用。 -
什么是RTTI?
-
RTTI?是 C++ 提供的一種機制,允許程序在運行時獲取和操作對象的類型信息。它讓程序能夠動態地確定對象的實際類型,即使是通過基類指針或引用來操作對象。
-
typeid運算符:用于獲取對象的類型信息,返回一個?
std::type_info
?對象的引用。 -
dynamic_cast:用于在繼承層次中進行安全的類型轉換,依賴于 RTTI 信息。
-
注意:C/C++都不是類型安全的語言。