目錄
一、C語言中的類型轉換
二、C++中的四個類型轉換
2.1 static_cast
2.2 dynamic_cast?
2.3 const_cast?
2.4? reinterpret_cast
2.5 總結?
結語
一、C語言中的類型轉換
在C語言中,如果賦值運算符左右兩側類型不同,或者形參與實參類型不匹配,或者返回值類型與接收返回值類型不一致時,就需要發生類型轉化,C語言中總共有兩種形式的類型轉換:隱式類型轉換?和?顯式類型轉換。
隱式類型轉換(截斷或提升):
編譯器在編譯階段自動進行,能轉就轉,不能轉就編譯失敗
double pi = 3.14159;
int truncatedPi = pi; // 隱式轉換,丟失小數部分
printf("Pi: %d\n", truncatedPi); // 輸出:3
return 0;
隱式轉換可能導致數據丟失:在隱式類型轉換中,可能會將一個較大范圍的數據類型轉換為一個較小范圍的類型,導致數據丟失或精度損失。例如,
double
?轉換為?int
,會丟失小數部分。?
顯式類型轉換(強轉):
用戶自己手動強制轉換變量類型
int num = 100;
int *int_ptr = #
void *void_ptr = (void*)int_ptr; // 將int指針轉換為void指針
int *new_int_ptr = (int*)void_ptr; // 再將void指針轉換回int指針
常見場景:
- 數值類型轉換
- 指針類型轉換
- 函數參數類型適配
注意:
- 數據截斷風險
在把大范圍的數據類型轉換為小范圍的數據類型(例如從long
轉換為int
)時,可能會導致數據截斷。- 浮點精度丟失
將double
類型轉換為float
類型時,可能會造成精度的損失。- 指針轉換的危險性
錯誤的指針類型轉換可能會使程序崩潰或者產生未定義行為。
二、C++中的四個類型轉換
C風格的轉換格式很簡單,但是有不少缺點的:
1. 隱式類型轉化有些情況下可能會出問題:比如數據精度丟失
2. 顯式類型轉換將所有情況混合在一起,代碼不夠清晰
因此C++提出了自己的類型轉換風格,但C++中仍然可以使用C的類型轉換。
在 C++ 里,為了進行類型轉換,提供了四個功能不同的類型轉換運算符,分別是static_cast
、dynamic_cast
、const_cast
以及reinterpret_cast
。
2.1 static_cast
static_cast主要用于進行良性轉換,非多態類型的轉換(靜態轉換),編譯器隱式執行的任何類型轉換都可用static_cast,但它不能用于兩個不相關的類型進行轉換。它在編譯階段就會完成類型檢查。
使用格式:
static_cast<new_type>(expression)
使用場景:
- 基本數據類型之間的轉換
double d = 3.14;
int i = static_cast<int>(d); // 把double類型轉換為int類型,值為3
- 子類到父類的向上轉型
class Base {};
class Derived : public Base {};Derived d;
Base* b = static_cast<Base*>(&d); // 安全的向上轉型
- 顯式調用轉換構造函數或者轉換運算符
class Fraction
{
public:Fraction(int num, int den = 1) : numerator(num), denominator(den) {}operator double() const { return (double)numerator / denominator; }
private:int numerator;int denominator;
};Fraction f(3, 4);
double d = static_cast<double>(f); // 調用operator double()
2.2 dynamic_cast?
dynamic_cast
主要用于進行安全的向下轉型(將父類指針或引用轉換為子類指針或引用),它會在運行時進行類型檢查。如果轉換不安全,對于指針會返回nullptr
,對于引用則會拋出std::bad_cast
異常。
使用格式:
dynamic_cast<new_type>(expression)
使用場景:
- 安全的向下轉型
class Base { virtual void dummy() {} }; // 必須有虛函數,才能進行RTTI
class Derived : public Base {};Base* b = new Derived;
Derived* d = dynamic_cast<Derived*>(b); // 轉換成功,d不為nullptr
- 交叉轉型?
class Base { virtual void dummy() {} };
class Derived1 : public Base {};
class Derived2 : public Base {};
class Derived3 : public Derived1, public Derived2 {};Derived3* d3 = new Derived3;
Derived1* d1 = dynamic_cast<Derived1*>(d3); // 轉換成功
Derived2* d2 = dynamic_cast<Derived2*>(d1); // 交叉轉型,d2不為nullptr
2.3 const_cast?
const_cast
專門用于修改類型的const
或volatile
屬性,主要用于去除const
限定(但要注意,通過這種方式修改原本為const
的對象是未定義行為)。
使用格式:
const_cast<new_type>(expression)
使用場景:
- 去除函數參數的
const
屬性
void print(char* str)
{printf("%s\n", str);
}const char* cstr = "Hello";
char* str = const_cast<char*>(cstr); // 去除const限定
// print(cstr); // 錯誤:不能將const char*傳遞給char*
print(str); // 正確
- 在
const
成員函數中修改對象狀態
class Counter
{
public:void increment() const {// count++; // 錯誤:在const成員函數中不能修改成員變量const_cast<int&>(count)++; // 正確:去除const限定}
private:int count;
};
2.4? reinterpret_cast
reinterpret_cast
用于進行低級的類型重新解釋,比如將指針轉換為整數,或者將一種類型的指針轉換為另一種不相關類型的指針。
使用格式:
reinterpret_cast<new_type>(expression)
使用場景:
- 指針和整數之間的轉換?
int* ptr = new int(10);
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr); // 將指針轉換為整數
int* new_ptr = reinterpret_cast<int*>(addr); // 將整數轉換回指針
- 不相關類型指針之間的轉換
int num = 10;
int* int_ptr = #
char* char_ptr = reinterpret_cast<char*>(int_ptr); // 將int指針轉換為char指針
- 函數指針類型的轉換
typedef void (*FuncPtr)();int add(int a, int b)
{ return a + b;
}FuncPtr func = reinterpret_cast<FuncPtr>(add); // 函數指針類型轉換
// func(); // 調用會導致未定義行為
2.5 總結?
轉換運算符 | 主要用途 | 安全性 |
---|---|---|
static_cast | 基本類型轉換、向上 / 向下轉型(不進行運行時檢查) | 中等 |
dynamic_cast | 安全的向下轉型、交叉轉型(運行時類型檢查) | 高 |
const_cast | 修改const /volatile 屬性 | 低(容易導致未定義行為) |
reinterpret_cast | 低級指針類型重新解釋、指針與整數轉換 | 極低(非常危險) |
結語
在實際編程中,優先使用C++風格的類型轉換,避免使用C風格的強制類型轉換,提高代碼的安全性和可讀性,這種用法會在Linux編程中見到。
下一篇將會介紹C++中IO流相關的知識,可以看作文件操作,有興趣的朋友可以關注一下。