dynamic_cast 转换

出自cppreference.com


 
 
C++ 語言
 
 

沿繼承層級向上、向下及側向,安全地轉換到其他類的指針和引用。

語法

dynamic_cast< 目標類型 >( 表達式 )
目標類型 - 指向完整類類型的指針,到完整類類型的引用,或指向(可有 cv 限定的)void 的指針
表達式 - 如果目標類型 是引用,那麼是完整類類型的左值(C++11 前)泛左值(C++11 起)表達式,如果目標類型 是指針,那麼是指向完整類類型的指針純右值

解釋

為描述方便起見,「表達式 或結果是到 T 的引用」表示「它是 T 類型的泛左值」,這遵循 decltype 的約定(C++11 前)

只有下列轉換在不移除常量性(或易變性)的場合才能用 dynamic_cast 進行。

1) 如果表達式 的類型剛好是目標類型 或目標類型 的更少 cv 限定版本,那麼結果是表達式 具有目標類型 類型的值。也就是說,dynamic_cast 可以用來添加常量性。隱式轉換和 static_cast 也能進行此轉換。
2) 如果目標類型 是「到(可有 cv 限定的)Base 的指針」、表達式 的類型是「到(可有 cv 限定的)Derived 的指針」,並且 BaseDerived 的基類,那麼:
  • 如果表達式 是空指針值,那麼結果也是空指針值。
  • 否則,結果是到表達式 指向的 Derived 對象的唯一 Base 子對象的指針。換言之,dynamic_cast 可以用於從派生類到基類向上轉換指針。隱式轉換和 static_cast 也能進行此轉換。
3) 如果目標類型 是「到(可有 cv 限定的)Base 的引用」、表達式 的類型是「到(可有 cv 限定的)Derived 的引用」,並且 BaseDerived 的基類,那麼結果是到表達式 指向的 Derived 對象的唯一 Base 子對象的引用。換言之,dynamic_cast 可以用於從派生類到基類向上轉換引用。隱式轉換和 static_cast 也能進行此轉換。
4) 如果表達式 是多態類型的空指針值,那麼結果是目標類型 類型的空指針值。
5) 否則,表達式 必須為指針或引用,且指代類型與表達式 的類型相似的對象,並且該對象必須在自己的生存期內或者正在構造或析構(否則行為未定義)。
a) 如果目標類型 是「到(可有 cv 限定的)void 的指針」,那麼結果是指向表達式 所指向的最終派生對象的指針。
b) 否則,會進行運行時檢查以確認表達式 指向/引用的對象是否可以轉換到目標類型 指向/引用的類型。
i)表達式 指向/引用的最終派生對象中,如果表達式 指向/引用某個目標類型  類型對象的某個公開基類子對象,並且只有一個目標類型  對象從該子對象派生,那麼轉換結果會指向/引用該 目標類型 對象。換言之,dynamic_cast 可以用於從基類到派生類的向下轉換
ii) 否則,如果表達式 指向/引用某個最終派生對象的公開基類子對象,並且該最終派生對象擁有無歧義公開基類目標類型 ,那麼轉換結果會指向/引用該最終派生對象的 目標類型 子對象。換言之,dynamic_cast 可以用於在同一基類的兩個派生類型之間的橫跨轉換(或側向轉換)。
iii) 否則,運行時檢查失敗。
  • 如果目標類型 是指針類型,那麼就會返回目標類型 類型的空指針值。
  • 如果目標類型 是引用類型,那麼就會拋出可以與 std::bad_cast 類型處理塊匹配的異常。

當在構造函數或析構函數中(直接或間接地)使用 dynamic_cast,且表達式 指代正在構造/銷毀的對象時,該對象被認為是最終派生對象。如果目標類型 不是到構造函數/析構函數自身的類或它的基類的指針或引用,那麼行為未定義。

與其他轉換表達式相似:

  • 目標類型 是引用類型時,結果是左值。
  • 目標類型 是指針類型時,結果是右值。
(C++11 前)
  • 目標類型 是左值引用類型(表達式 必然是左值)時,結果是左值。
  • 目標類型 是右值引用類型(表達式 是完整類類型,可以是左值或右值(C++17 前)必然是泛左值(純右值被實質化(C++17 起))時,結果是亡值。
  • 目標類型 是指針類型時,結果是純右值。
(C++11 起)

註解

static_cast 也能用來進行向下轉換,它不會有運行時檢查的開銷,但只有在程序(通過某些其他邏輯)能夠保證表達式 指向的對象肯定是 Derived 時才是安全的。

某些形式的 dynamic_cast 依賴於運行時類型鑑別,即編譯的程序中關於每個多態類的信息。編譯器通常有選項禁用此信息。

關鍵詞

dynamic_cast

示例

#include <iostream>

struct V
{
    virtual void f() {} // 必须为多态,以使用带运行时检查的 dynamic_cast
};

struct A : virtual V {};

struct B : virtual V
{
    B(V* v, A* a)
    {
        // 构造中转换(见后述 D 的构造函数中的调用)
        dynamic_cast<B*>(v); // 良好定义:v 有类型 V*,V 是 B 的基类,产生 B*
        dynamic_cast<B*>(a); // 未定义行为:a 有类型 A*,A 不是 B 的基类
    }
};

struct D : A, B
{
    D() : B(static_cast<A*>(this), this) {}
};

struct Base
{
    virtual ~Base() {}
};

struct Derived: Base
{
    virtual void name() {}
};

int main()
{
    D d; // 最终派生对象
    A& a = d; // 向上转换,可以用 dynamic_cast,但不是必须的
    
    [[maybe_unused]]
    D& new_d = dynamic_cast<D&>(a); // 向下转换
    [[maybe_unused]]
    B& new_b = dynamic_cast<B&>(a); // 侧向转换
    
    Base* b1 = new Base;
    if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
    {
        std::cout << "成功从 b1 向下转换到 d\n";
        d->name(); // 可以安全调用
    }
    
    Base* b2 = new Derived;
    if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
    {
        std::cout << "成功从 b2 向下转换到 d\n";
        d->name(); // 可以安全调用
    }
    
    delete b1;
    delete b2;
}

輸出:

成功从 b2 向下转换到 d

缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 出版時的行為 正確行為
CWG 1269 C++11 目標類型 是右值引用類型時不會為亡值表達式 進行運行時檢查 此時會進行運行時檢查
CWG 2861 C++98 表達式 可以指向/指代類型不可訪問的對象 此時行為未定義

引用

  • C++23 標準(ISO/IEC 14882:2024):
  • 7.6.1.7 Dynamic cast [expr.dynamic.cast]
  • C++20 標準(ISO/IEC 14882:2020):
  • 7.6.1.6 Dynamic cast [expr.dynamic.cast]
  • C++17 標準(ISO/IEC 14882:2017):
  • 8.2.7 Dynamic cast [expr.dynamic.cast]
  • C++14 標準(ISO/IEC 14882:2014):
  • 5.2.7 Dynamic cast [expr.dynamic.cast]
  • C++11 標準(ISO/IEC 14882:2011):
  • 5.2.7 Dynamic cast [expr.dynamic.cast]
  • C++98 標準(ISO/IEC 14882:1998):
  • 5.2.7 Dynamic cast [expr.dynamic.cast]
  • C++03 標準(ISO/IEC 14882:2003):
  • 5.2.7 Dynamic cast [expr.dynamic.cast]

參閱