Namespaces
Variants

dynamic_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

安全地在继承层次结构中向上、向下及横向转换类和引用的指针。

目录

语法

dynamic_cast< 目标类型 >( 表达式 )
目标类型 - 指向完整类类型的指针、完整类类型的引用,或指向(可选地带有 cv 限定符) void 的指针
表达式 - 左值 (C++11 前) 泛左值 (C++11 起) (当 目标类型 为引用时需为完整类类型);纯右值(当 目标类型 为指针时需为指向完整类类型的指针)

说明

为便于描述,“ 表达式 或结果为 T 的引用”意指“它是 T 类型的泛左值” ,这遵循了 decltype 的约定 (C++11 起)

只有以下转换可以通过 dynamic_cast 实现,除非这些转换会 去除常量性 (或易变性)。

1) expression 的类型恰好是 target-type 或其 cv 限定更少的版本,则结果为类型为 target-type expression 值。换言之, dynamic_cast 可用于 添加常量性 。隐式转换和 static_cast 同样可以执行此转换。
2) target-type 为“指向(可能带 cv 限定符) Base 的指针”,且 expression 的类型为“指向(可能带 cv 限定符) Derived 的指针”,且 Base Derived 的基类,则结果为:
  • expression 为空指针值,则结果为空指针值;否则
  • 结果为指向由 expression 所指 Derived 对象中唯一 Base 子对象 的指针。换言之, dynamic_cast 可用于对指针进行 向上转型 (从派生类到基类)。隐式转换和 static_cast 同样可以执行此转换。
3) target-type 为“(可能带有 cv 限定符的) Base 的引用”,且 expression 的类型为“(可能带有 cv 限定符的) Derived ”,其中 Base Derived 的基类,则结果为 expression 所引用的 Derived 对象中唯一的 Base 子对象。换言之, dynamic_cast 可用于对引用进行 向上转型 (从派生类到基类)。隐式转换和 static_cast 同样可以执行此转换。
4) expression 是多态类型的空指针值,则结果为 target-type 的空指针值。
5) 否则, expression 必须在其 生存期 内或构造/析构期间,指向或引用一个 多态类型 对象,且该对象的类型与 expression 的类型 相似 (否则行为未定义)
a) expression 为指向(可能带 cv 限定) void 的指针,则结果为指向 expression 所指向的 最终派生对象 的指针。
b) 否则将应用运行时检查,以验证由 expression 指向/引用的对象是否能够转换为由 target-type 指向或引用的 Target 类型:
i) 若在由 expression 指向/引用的最终派生对象中, expression 指向/引用的是 Target 对象的公开基类子对象,且仅存在一个 Target 类型对象派生自 expression 指向/引用的子对象,则结果指向/引用该 Target 对象。换言之, dynamic_cast 可用于将指针/引用从基类向派生类进行 向下转型
ii) 否则,若 expression 指向/引用最终派生对象的公开基类子对象,且最终派生对象的类型具有明确且公开的 Target 类型基类,则结果指向/引用最终派生对象的 Target 子对象。换言之, dynamic_cast 可用于在派生自同一基类的两种类型之间进行 交叉转换 (或侧向转换)指针/引用。
iii) 否则,运行时检查失败。
  • target-type 为指针类型,则结果为 target-type 的空指针值。
  • target-type 为引用类型,则抛出与类型为 std::bad_cast 异常处理器 匹配的异常。

当在构造函数或析构函数中(直接或间接)使用 dynamic_cast ,且 expression 指向当前正在构造/析构的对象时,该对象将被视为最终派生对象。如果 target-type 不是指向该构造函数/析构函数自身类或其某个基类的指针或引用,则行为未定义。

与其他类型转换表达式类似,其结果如下:

  • target-type 是引用类型,则为左值
  • target-type 是指针类型,则为右值
(C++11 前)
  • target-type 是左值引用类型,则为左值( expression 必须是左值)
  • target-type 是右值引用类型,则为亡值( expression 可以是左值或右值 (C++17 前) 必须是泛左值(纯右值会进行 临时物化 (C++17 起) ,且需为完整类类型)
  • target-type 是指针类型,则为纯右值
(C++11 起)

注释

向下转型也可使用 static_cast 执行,这种方式能避免运行时检查的开销,但仅当程序能(通过其他逻辑)保证 expression 所指向的对象确定是 Derived 类型时才是安全的。

某些形式的 dynamic_cast 依赖于 运行时类型识别 (RTTI),即编译程序中每个多态类的相关信息。编译器通常提供选项来禁用此类信息的包含。

关键词

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 << "downcast from b1 to d successful\n";
        d->name(); // 可安全调用
    }
    Base* b2 = new Derived;
    if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
    {
        std::cout << "downcast from b2 to d successful\n";
        d->name(); // 可安全调用
    }
    delete b1;
    delete b2;
}

输出:

downcast from b2 to d successful

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 应用于 发布时的行为 正确行为
CWG 1269 C++11 目标类型 为右值引用类型时,未对xvalue
表达式 执行运行时检查
执行检查
CWG 2861 C++98 表达式 可能指向/引用类型不可访问的对象 此情况下的行为未定义

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 7.6.1.7 动态转换 [expr.dynamic.cast]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 7.6.1.6 动态转换 [expr.dynamic.cast]
  • C++17 标准 (ISO/IEC 14882:2017):
  • 8.2.7 动态转换 [expr.dynamic.cast]
  • C++14 标准 (ISO/IEC 14882:2014):
  • 5.2.7 动态转换 [expr.dynamic.cast]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 5.2.7 动态转换 [expr.dynamic.cast]
  • C++98 标准 (ISO/IEC 14882:1998):
  • 5.2.7 动态转换 [expr.dynamic.cast]
  • C++03 标准 (ISO/IEC 14882:2003):
  • 5.2.7 动态转换 [expr.dynamic.cast]

参见