dynamic_cast的理解

dynamic_cast:(具体使用就不详细说明了)

C++ 中用于 安全的类层次结构转换 的类型转换运算符,主要用于 多态类型(即包含虚函数的类)的指针或引用之间的转换

前提条件:

必须要有虚函数才能使用 dynamic_cast。即使存在单纯的继承关系,如果基类没有虚函数,dynamic_cast 无法使用

原因:

1. 为什么必须要有虚函数?

dynamic_cast 依赖 运行时类型信息(RTTI),而 RTTI 是通过虚函数表(vtable)实现的。

  • 虚函数表(vtable):当一个类包含虚函数时,编译器会为该类生成虚函数表,并在对象中插入指向该表的指针(vptr)。
  • RTTI 的存储 :虚函数表中会保存类的类型信息(如类型名称、继承关系等),供 dynamic_cast 在运行时查询。
  • 没有虚函数 → 没有虚函数表 → 没有 RTTI → dynamic_cast 无法工作

2. 示例验证

代码示例(基类无虚函数)
cpp 复制代码
#include <iostream>

class Base {};  // 基类没有虚函数
class Derived : public Base {};

int main() {
    Base* base = new Derived();
    
    // 尝试使用 dynamic_cast(编译错误!)
    Derived* derived = dynamic_cast<Derived*>(base);  
    
    delete base;
    return 0;
}
编译错误信息
text 复制代码
error: 'Base' is not polymorphic
     Derived* derived = dynamic_cast<Derived*>(base);
                                     ^

3. 单纯继承无法使用 dynamic_cast

即使存在继承关系,只要基类没有虚函数:

  • dynamic_cast 无法编译通过,因为基类不是多态类型。
  • 无法进行安全的运行时类型检查

工作原理:

首先,dynamic_cast的核心在于运行时类型检查。这通常通过对象的虚函数表(vtable)中的RTTI指针来实现。每个多态类(即有虚函数的类)的虚函数表都包含一个指向type_info的指针,该指针提供了类的类型信息。当进行dynamic_cast时,编译器生成的代码会遍历继承树,检查目标类型是否是当前类型或其派生类(个人觉得是比较 hash_code(), 而不是name())

cpp 复制代码
class type_info
{
public:

    type_info(const type_info&) = delete;
    type_info& operator=(const type_info&) = delete;

    size_t hash_code() const noexcept
    {
        return __std_type_info_hash(&_Data);
    }

    bool operator==(const type_info& _Other) const noexcept
    {
        return __std_type_info_compare(&_Data, &_Other._Data) == 0;
    }

#if !_HAS_CXX20
    bool operator!=(const type_info& _Other) const noexcept
    {
        return __std_type_info_compare(&_Data, &_Other._Data) != 0;
    }
#endif // !_HAS_CXX20

    bool before(const type_info& _Other) const noexcept
    {
        return __std_type_info_compare(&_Data, &_Other._Data) < 0;
    }

    const char* name() const noexcept
    {
        #ifdef _M_CEE_PURE
        return __std_type_info_name(&_Data, static_cast<__type_info_node*>(__type_info_root_node.ToPointer()));
        #else
        return __std_type_info_name(&_Data, &__type_info_root_node);
        #endif
    }

    const char* raw_name() const noexcept
    {
        return _Data._DecoratedName;
    }

    virtual ~type_info() noexcept;

private:

    mutable __std_type_info_data _Data;
};

性能开销来源:

  • 虚表访问: 需通过虚表获取 type_info,可能引发缓存未命中。
cpp 复制代码
// 伪代码:获取动态类型
const __class_type_info* __get_dynamic_type(const void* obj) {
    // 获取虚表指针(通常位于对象起始位置)
    const void** vtable = *reinterpret_cast<const void***>(obj);
    // 虚表中存储了 type_info 指针(通常位于固定位置,如 -1 偏移)
    const __class_type_info* type_info = vtable[-1];
    return type_info;
}
  • 继承链遍历: 递归检查继承关系的复杂度为 O(继承深度)。
cpp 复制代码
// 伪代码:检查继承关系
bool __do_find(const __class_type_info* target) const {
    if (this == target) {
        return true;  // 当前类型就是目标类型
    }

    // 遍历基类列表
    if (is_single_inheritance()) {
        return __base_type->__do_find(target);  // 单继承递归
    } else {
        for (each base in __base_info) {         // 多继承递归
            if (base.__base_type->__do_find(target)) {
                return true;
            }
        }
    }
    return false;
}
  • 指针调整计算: 多重继承中可能需要复杂偏移计算。(在多重继承中,不同基类的子对象可能有不同的偏移量,需调整指针:)
cpp 复制代码
// 伪代码:调整指针偏移
void* adjust_pointer(const void* src, const __class_type_info* src_type, const __class_type_info* target_type) {
    // 计算从源类型到目标类型的偏移量
    ptrdiff_t offset = calculate_offset(src_type, target_type);
    return reinterpret_cast<char*>(src) + offset;
}

判断两个类是否有继承关系:

方法 检查时机 适用范围 优点 缺点
std::is_base_of 编译时 任意类之间的继承关系 简单、标准 不能检查多态对象的具体类型
typeiddynamic_cast 运行时 多态类型的具体对象 可以检查实际对象类型 依赖 RTTI,性能开销

推荐方法

  • 编译时检查 :优先使用 std::is_base_of
  • 运行时检查 :使用 typeiddynamic_cast(需多态类型)。
相关推荐
岑梓铭5 分钟前
考研408《计算机组成原理》复习笔记,第三章(3)——多模块存储器
笔记·考研·408·计算机组成原理
菜菜why18 分钟前
MSPM0G3507学习笔记(一) 重置版:适配逐飞库的ti板环境配置
笔记·学习·电赛·嵌入式软件·mspm0
c76938 分钟前
【文献笔记】Automatic Chain of Thought Prompting in Large Language Models
人工智能·笔记·语言模型·论文笔记
Dovis(誓平步青云)40 分钟前
基于探索C++特殊容器类型:容器适配器+底层实现原理
开发语言·c++·queue·适配器·stack
X_StarX1 小时前
【Unity笔记01】基于单例模式的简单UI框架
笔记·ui·unity·单例模式·游戏引擎·游戏开发·大学生
pipip.2 小时前
UDP————套接字socket
linux·网络·c++·网络协议·udp
智者知已应修善业3 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
孞㐑¥7 小时前
Linux之Socket 编程 UDP
linux·服务器·c++·经验分享·笔记·网络协议·udp
sealaugh329 小时前
aws(学习笔记第四十八课) appsync-graphql-dynamodb
笔记·学习·aws
水木兰亭10 小时前
数据结构之——树及树的存储
数据结构·c++·学习·算法