第 17 節

C++類型轉換

0瀏覽次數0訪問次數--跳出率--平均停留

本節解決什麼問題

C++ 中經常會遇到“把一種類型當成另一種類型使用”的場景,例如:

  • double 轉成 int
  • enum class 轉成整數。
  • 把基類指針安全地轉成派生類指針。
  • 臨時去掉 const 限定,適配舊接口。
  • 在底層代碼裏把指針、整數、字節視圖互相轉換。

C 風格強轉寫法 (目标类型)表达式 能做很多事,但問題也在這裏:它太“萬能”,讀代碼的人很難一眼看出這次轉換到底是普通數值轉換、繼承層級轉換、去掉 const,還是危險的底層重解釋。

C++ 提供了四個更明確的類型轉換運算符,讓轉換意圖寫在代碼裏。

這個特性是什麼

C++ 的四種顯式類型轉換是:

轉換方式主要用途安全程度
static_cast<T>(expr)常規類型轉換,如數值、枚舉、父子類上行轉換常用,編譯期檢查
dynamic_cast<T>(expr)多態類型的安全向下轉換運行期檢查,失敗返回 nullptr 或拋異常
const_cast<T>(expr)添加或移除 const / volatile 限定要非常剋制
reinterpret_cast<T>(expr)底層二進制重解釋,如指針和整數互轉最危險,少用

原則:能不用強轉就不用;必須強轉時,優先選擇語義最窄、最能說明意圖的轉換。

C++ 標準版本

C++98 就已經提供這四種轉換寫法,實際工程中依然推薦使用它們替代 C 風格強轉。

需要的頭文件

類型轉換本身不需要額外頭文件。示例代碼使用到的庫需要對應頭文件,例如:

#include <cstdint>
#include <iostream>

運行/觀察結果: 這段是頭文件示例,放到完整程序頂部即可。

基本語法

目标类型 value = static_cast<目标类型>(表达式);
目标类型 value = dynamic_cast<目标类型>(表达式);
目标类型 value = const_cast<目标类型>(表达式);
目标类型 value = reinterpret_cast<目标类型>(表达式);

運行/觀察結果: 這段是語法格式說明,重點看四種轉換的寫法差異。

示例代碼

示例 1:static_cast 處理常規轉換

static_cast 適合明確、常規、編譯期能檢查的轉換。比如數值轉換、enum class 轉整數、父類指針指向子類對象等。

#include <iostream>

enum class Status
{
    ok = 200,
    not_found = 404
};

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    double score = 89.7;
    int integer_score = static_cast<int>(score);

    Status status = Status::not_found;
    int status_code = static_cast<int>(status);

    std::cout << "integer_score = " << integer_score << "\n";
    std::cout << "status_code = " << status_code << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

運行結果

integer_score = 89
status_code = 404

示例 2:dynamic_cast 安全判斷真實對象類型

dynamic_cast 常用於多態基類。向下轉換時,它會在運行期檢查對象真實類型。

#include <iostream>

struct Animal
{
    virtual ~Animal() = default;
};

struct Cat : Animal
{
    void meow() const
    {
        std::cout << "cat: meow\n";
    }
};

struct Dog : Animal
{
};

void try_meow(Animal* animal)
{
    Cat* cat = dynamic_cast<Cat*>(animal);

    if (cat != nullptr)
    {
        cat->meow();
    }
    else
    {
        std::cout << "not a cat\n";
    }
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    Cat cat;
    Dog dog;

    try_meow(&cat);
    try_meow(&dog);

    // 返回 0 表示程序正常结束。
    return 0;
}

運行結果

cat: meow
not a cat

注意:dynamic_cast 用在指針上,轉換失敗會得到 nullptr;用在引用上,轉換失敗會拋出 std::bad_cast。它要求基類至少有一個虛函數,通常是虛析構函數。

示例 3:const_cast 只改變 const 限定

const_cast 只能添加或移除 const / volatile,不能把 int 轉成 double,也不能把不相關類型互轉。

#include <iostream>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    int value = 10;
    const int& readonly_ref = value;

    int& writable_ref = const_cast<int&>(readonly_ref);
    writable_ref = 20;

    std::cout << "value = " << value << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

運行結果

value = 20

這個例子能修改成功,是因爲真正的原始對象 value 不是 const,只是通過 const int& 觀察它。如果原始對象本身就是 const,再用 const_cast 強行修改就是未定義行爲。

示例 4:reinterpret_cast 做底層重解釋

reinterpret_cast 表示“把這段二進制數據按另一種類型解釋”。它繞過了很多類型系統保護,通常只應該出現在非常底層的代碼裏。

#include <cstdint>
#include <iostream>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    int value = 42;
    int* p = &value;

    std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(p);
    int* again = reinterpret_cast<int*>(raw);

    std::cout << "*again = " << *again << "\n";
    std::cout << std::boolalpha;
    std::cout << "same pointer = " << (p == again) << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

運行結果

*again = 42
same pointer = true

這個例子只用於說明“指針可以轉成能容納指針的整數,再轉回來”。不要隨便拿轉換後的整數做地址計算,也不要把不相關對象強行解釋成另一個對象類型。

四種轉換怎麼選

需求推薦寫法說明
doubleintstatic_cast<int>(x)明確可能丟失小數
enum class 轉整數static_cast<int>(e)枚舉類不會隱式轉整數
基類指針轉派生類指針,並且不確定真實類型dynamic_cast<Derived*>(p)轉換失敗可判斷 nullptr
去掉 const 適配舊接口const_cast<T*>(p)前提是舊接口不會修改真正的 const 對象
指針和整數互轉、底層字節解釋reinterpret_cast<T>(x)非常底層,優先避免

C風格強轉爲什麼不推薦

C 風格強轉寫起來短:

int n = (int)3.14;

運行/觀察結果: 這段會把 3.14 轉成 3,但讀代碼時看不出它屬於哪類轉換。

更推薦寫成:

int n = static_cast<int>(3.14);

運行/觀察結果: 這段同樣會得到 3,但轉換意圖更明確:這是常規數值轉換。

C 風格強轉可能在背後組合多種轉換能力,既可能像 static_cast,也可能像 const_cast,甚至接近 reinterpret_cast。代碼越底層、類型越複雜,這種“不說明白”的風險就越大。

常見錯誤

用 static_cast 做不安全向下轉換

Animal* animal = new Dog;
Cat* cat = static_cast<Cat*>(animal);  // 危险:编译可能通过,但真实对象不是 Cat

運行/觀察結果: 這段是錯誤示例,真實對象類型不匹配時繼續使用 cat 會產生未定義行爲。

如果不確定真實類型,應該使用 dynamic_cast

修改真正的 const 對象

const int value = 10;
int& ref = const_cast<int&>(value);
ref = 20;  // 未定义行为

運行/觀察結果: 這段是錯誤示例,const_cast 可以騙過編譯器,但不能讓真正的常量安全地變成可修改對象。

濫用 reinterpret_cast

double d = 3.14;
int* p = reinterpret_cast<int*>(&d);  // 危险:把 double 对象当 int 对象访问

運行/觀察結果: 這段是錯誤示例,違反類型別名規則或對象表示假設時,行爲不可預測。

使用建議

  1. 普通轉換優先用 static_cast
  2. 多態向下轉換優先用 dynamic_cast,並檢查失敗情況。
  3. const_cast 只用於適配舊接口,不要用它修改真正的 const 對象。
  4. reinterpret_cast 只放在非常底層、邊界清晰的代碼裏,並集中封裝。
  5. 不要爲了少打幾個字使用 C 風格強轉。

小結

  • static_cast:常規轉換,最常用。
  • dynamic_cast:多態類型安全向下轉換。
  • const_cast:只改變 const / volatile 限定。
  • reinterpret_cast:底層重解釋,風險最高。

一句話記憶:能不用就不用;必須用時,把轉換意圖寫清楚。

音乐页