dynamic_cast 转换
出自cppreference.com
沿繼承層級向上、向下及側向,安全地轉換到其他類的指針和引用。
語法
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 的指針」,並且 Base 是 Derived 的基類,那麼:
- 如果表達式 是空指針值,那麼結果也是空指針值。
- 否則,結果是到表達式 指向的
Derived對象的唯一Base子對象的指針。換言之,dynamic_cast可以用於從派生類到基類向上轉換指針。隱式轉換和static_cast也能進行此轉換。
3) 如果目標類型 是「到(可有 cv 限定的)
Base 的引用」、表達式 的類型是「到(可有 cv 限定的)Derived 的引用」,並且 Base 是 Derived 的基類,那麼結果是到表達式 指向的 Derived 對象的唯一 Base 子對象的引用。換言之,dynamic_cast 可以用於從派生類到基類向上轉換引用。隱式轉換和 static_cast 也能進行此轉換。b) 否則,會進行運行時檢查以確認表達式 指向/引用的對象是否可以轉換到目標類型 指向/引用的類型。
i) 在表達式 指向/引用的最終派生對象中,如果表達式 指向/引用某個目標類型 類型對象的某個公開基類子對象,並且只有一個目標類型 對象從該子對象派生,那麼轉換結果會指向/引用該 目標類型 對象。換言之,
dynamic_cast 可以用於從基類到派生類的向下轉換。ii) 否則,如果表達式 指向/引用某個最終派生對象的公開基類子對象,並且該最終派生對象擁有無歧義公開基類目標類型 ,那麼轉換結果會指向/引用該最終派生對象的 目標類型 子對象。換言之,
dynamic_cast 可以用於在同一基類的兩個派生類型之間的橫跨轉換(或側向轉換)。iii) 否則,運行時檢查失敗。
- 如果目標類型 是指針類型,那麼就會返回目標類型 類型的空指針值。
- 如果目標類型 是引用類型,那麼就會拋出可以與 std::bad_cast 類型處理塊匹配的異常。
當在構造函數或析構函數中(直接或間接地)使用 dynamic_cast,且表達式 指代正在構造/銷毀的對象時,該對象被認為是最終派生對象。如果目標類型 不是到構造函數/析構函數自身的類或它的基類的指針或引用,那麼行為未定義。
與其他轉換表達式相似:
|
(C++11 前) |
|
(C++11 起) |
註解
static_cast 也能用來進行向下轉換,它不會有運行時檢查的開銷,但只有在程序(通過某些其他邏輯)能夠保證表達式 指向的對象肯定是 Derived 時才是安全的。
某些形式的 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]