当前位置: 首页 > news >正文

RTTI(运行时类型识别)

RTTI(运行时类型识别)

一、RTTI 概述

RTTI(Run-Time Type Identification,运行时类型识别)是 C++ 中允许程序在运行时获取对象实际类型信息的机制

它主要通过两个操作符实现:

操作符作用
typeid查询表达式的类型信息,返回type_info对象
dynamic_cast在类层次结构中进行安全的向下转换
// RTTI 的核心作用classBase{virtual~Base(){}};classDerived:publicBase{};Base*p=newDerived();// 通过 p 只能看到 Base 的接口,但 RTTI 可以知道实际类型是 Derivedcout<<typeid(*p).name()<<endl;// 输出 "Derived"Derived*d=dynamic_cast<Derived*>(p);// 安全转换

二、typeid 操作符

2.1 基本语法与用法

#include<typeinfo>intmain(){int*ip=newint(10);intar[10];int(*par)[10]=&ar;// 指向数组的指针// typeid 返回 type_info 对象,name() 返回类型名称cout<<typeid(ip).name()<<endl;// int* (具体输出取决于编译器)cout<<typeid(*ip).name()<<endl;// intcout<<typeid(par).name()<<endl;// int(*)[10]cout<<typeid(*par).name()<<endl;// int[10]return0;}

2.2 typeid 与 decltype 的区别

特性typeiddecltype
求值时机运行时(多态类型)
编译时(非多态类型)
编译时
返回类型type_info对象类型本身(用于声明)
是否计算表达式多态类型会计算,非多态不计算不计算
主要用途运行时类型识别编译时类型推导

2.3 忽略顶层 const 和 volatile

intmain(){constinta=10;intb=20;constint*pa=&a;int*pb=&b;// 指针本身的类型:pa 和 pb 类型不同(const int* 与 int*)cout<<(typeid(pa)==typeid(pb))<<endl;// false// 指向的对象类型:去除顶层 const 后相同(int 与 int)cout<<(typeid(*pa)==typeid(*pb))<<endl;// true// const 变量本身constintc=10;intd=20;cout<<(typeid(c)==typeid(d))<<endl;// true(const 被忽略)return0;}

规则typeid会忽略表达式的顶层constvolatile限定符。


2.4 多态类型与非多态类型

classObject{intvalue;public:Object(intx=0):value(x){}// 没有虚函数 → 非多态类型};classBase:publicObject{intnum;public:Base(intx=0):num(x){}};intmain(){Base base;Object*op=&base;// 非多态类型:typeid 在编译时确定,返回静态类型cout<<typeid(*op).name()<<endl;// Object(不是 Base!)return0;}// =========================================== 添加虚函数后 ============================================classObject{public:virtualvoidfun(){}// 有了虚函数 → 多态类型virtual~Object(){}};classBase:publicObject{public:// ...};intmain(){Base base;Object*op=&base;// 多态类型:typeid 在运行时确定,返回动态类型cout<<typeid(*op).name()<<endl;// Base(运行时确定)return0;}

关键区别

typeid 对多态/非多态类型的处理 1. 非多态类型(无虚函数) typeid(*op) 在编译时确定 → 总是返回编译时类型(Object) 即使 op 指向派生类对象 2. 多态类型(有虚函数) typeid(*op) 在运行时确定 → 通过虚表查找实际类型 返回 op 实际指向的对象类型(Base)

2.5 type_info 类

typeid返回std::type_info类型的对象,该类提供了类型信息的接口:

#include<typeinfo>classtype_info{public:// 比较类型是否相同booloperator==(consttype_info&rhs)constnoexcept;booloperator!=(consttype_info&rhs)constnoexcept;// 返回类型的可读名称(编译器相关)constchar*name()constnoexcept;// 返回类型在编译器中是否被排序(用于 map 等容器)size_thash_code()constnoexcept;// 返回是否在转换为目标类型前boolbefore(consttype_info&rhs)constnoexcept;};

使用示例

intmain(){inta=10;constint&b=a;consttype_info&ti1=typeid(a);consttype_info&ti2=typeid(b);// 比较类型cout<<std::boolalpha;cout<<(ti1==ti2)<<endl;// true(忽略引用和 const)// 获取类型名称cout<<ti1.name()<<endl;// int(具体输出取决于编译器)// 获取哈希值cout<<ti1.hash_code()<<endl;return0;}

2.6 对空指针解引用的处理

intmain(){Object*op=nullptr;// 安全:对指针本身求 typeid(指针类型是编译时已知)cout<<typeid(op).name()<<endl;// Object*// 危险:对空指针解引用try{cout<<typeid(*op).name()<<endl;// 抛出 std::bad_typeid 异常}catch(conststd::bad_typeid&e){cout<<"异常: "<<e.what()<<endl;}return0;}

规则:对空指针解引用使用typeid会抛出std::bad_typeid异常。

三、dynamic_cast

3.1 概述

dynamic_cast是 C++ 中唯一一个运行时执行类型转换的运算符,用于在类层次结构中进行安全的向下转换

// 语法dynamic_cast<目标类型>(表达式)// 特点:// 1. 必须在运行时执行(需要 RTTI 支持)// 2. 必须有虚函数(多态类型)// 3. 必须是公有继承

3.2 上行转换(Derived → Base)

classObject{public:virtual~Object(){}virtualvoidfun(){cout<<"Object::fun"<<endl;}};classBase:publicObject{public:voidfunc(){cout<<"Base::func"<<endl;}};intmain(){Base base;// 上行转换:static_cast 和 dynamic_cast 效果相同Object*p1=static_cast<Object*>(&base);Object*p2=dynamic_cast<Object*>(&base);Object&r1=static_cast<Object&>(base);Object&r2=dynamic_cast<Object&>(base);return0;}

3.3 下行转换(Base → Derived)—— 指针版本

intmain(){Base base;// 真正的 Base 对象Object obj;// 真正的 Object 对象Object*p1=&base;// 指向 Base 对象Object*p2=&obj;// 指向 Object 对象// 情况1:实际类型匹配Base*bp1=dynamic_cast<Base*>(p1);// 成功,返回有效指针cout<<"bp1 = "<<bp1<<endl;// 情况2:实际类型不匹配:指针所指向对象的实际类型与要转换的目标类型不一致// dynamic_cast 会检查 p2 所指向对象的实际类型。因 p2 实际指向是 Object 类型的对象,而转换的目标类型是 Base 类型// 实际类型与目标类型不匹配,所以转换失败,bp2 会被赋值为 nullptr。Base*bp2=dynamic_cast<Base*>(p2);// 失败,返回 nullptrcout<<"bp2 = "<<bp2<<endl;// 输出 0// static_cast 不会检查,返回无效指针(危险)// static_cast 是一种静态类型转换,不会在运行时检查指针所指向对象的实际类型。// 即使 p2 实际指向的是 Object 类型的对象,static_cast 也会直接进行转换,返回一个 Base 类型的指针。// 但这个指针实际上指向的是 Object 类型的对象,对其进行 Base 类型相关的操作会导致未定义行为,是非常危险的。Base*bp3=static_cast<Base*>(p2);// 危险!未定义行为return0;}

3.4 下行转换(Base → Derived)—— 引用版本

intmain(){Object obj;Base base;Object&r1=base;// 引用 Base 对象Object&r2=obj;// 引用 Object 对象// 情况1:成功Base&br1=dynamic_cast<Base&>(r1);// 成功// 情况2:失败,抛出 std::bad_cast 异常try{Base&br2=dynamic_cast<Base&>(r2);// 抛出异常}catch(conststd::bad_cast&e){cout<<"异常: "<<e.what()<<endl;}return0;}

3.5 dynamic_cast 行为总结

转换类型目标类型失败时行为
指针转换指针类型返回nullptr
引用转换引用类型抛出std::bad_cast

四、总结

4.1 typeid 总结

场景行为备注
非多态类型表达式编译时确定类型返回静态类型
多态类型表达式运行时确定类型通过虚表查找
指针本身编译时确定不涉及解引用
空指针解引用抛出std::bad_typeid需要 try-catch
顶层 const/volatile被忽略不影响类型比较

4.2 dynamic_cast 总结

场景行为失败返回
上行转换(派生→基类)总是成功
下行转换(基类→派生类)运行时检查指针:nullptr
引用:bad_cast
多态类型要求必须有虚函数否则编译错误
访问权限要求必须是公有继承否则编译错误

RTTI(运行时类型识别)内存布局

一、RTTI 与虚表的关系

RTTI 的实现与虚函数表(vtable)密切相关。当类中有虚函数时,编译器会在虚表中添加RTTI 信息指针,指向type_info对象。

带 RTTI 信息的虚表结构 Object 虚表 Base 虚表 ----------------- ------------------ 偏移 内容 偏移 内容 -8 RTTI 指针 -8 RTTI 指针 0 fun()地址 0 fun()地址 8 ~Object() 8 ~Base() ↑ ↑ Object 对象 Base 对象 ----------------- ----------------- _vfptr _vfptr value value num RTTI 指针指向 type_info 对象(只读数据段) type_info 对象包含: - 类型名称(mangled name) - 类型比较函数 - 类型哈希值

二、有无虚函数时的内存对比

2.1 无虚函数的类(非多态类型)

classObject{private:intvalue;public:Object(intx=0):value(x){}// 没有虚函数};classBase:publicObject{private:intnum;public:Base(intx=0):num(x){}// 没有虚函数};

内存布局

无虚函数的类内存布局: Object 对象 Base 对象 ------------------ ------------------- value (4B) Object 部分:value (4B) num (4B) 没有虚表指针 sizeof(Object) = 4 没有虚表指针 sizeof(Base) = 8 没有 RTTI 信息(无法运行时识别实际类型)

2.2 有虚函数的类(多态类型)

classObject{private:intvalue;public:Object(intx=0):value(x){}virtualvoidfun(){}// 虚函数virtual~Object(){}// 虚析构};classBase:publicObject{private:intnum;public:Base(intx=0):num(x){}voidfun()override{}~Base()override{}};

内存布局

三、typeid 的内存访问过程

3.1 对多态类型使用 typeid

intmain(){Base base;Object*op=&base;// 对多态类型解引用,typeid 需要访问虚表获取 RTTIconsttype_info&ti=typeid(*op);cout<<ti.name()<<endl;// 输出 "Base"return0;}

内存访问流程

步骤: 1. 通过 op 找到对象 2. 读取对象的 _vfptr 3. 从虚表偏移 -8 处读取 RTTI 指针 4. 通过 RTTI 指针访问 type_info 对象

3.2 对非多态类型使用 typeid

intmain(){Base base;Object*op=&base;// 但 Object 没有虚函数// 非多态类型:typeid 在编译时确定,不访问虚表consttype_info&ti=typeid(*op);// 返回 Object 的类型信息cout<<ti.name()<<endl;// 输出 "Object"return0;}

内存访问流程(编译时就确定,无运行时开销):

typeid 对非多态类型的访问过程 源代码:typeid(*op) 编译器分析:op 的类型是 Object*,而 Object 没有虚函数 → 静态类型就是实际类型 → 编译时确定类型为 Object 编译结果:直接替换为 Object 的 type_info 地址 (不需要运行时查找) const type_info& ti = __type_info_for_Object; 优点:无运行时开销 缺点:无法识别派生类的实际类型

四、两种类型的内存对比总结

特性非多态类型(无虚函数)多态类型(有虚函数)
对象内存只有数据成员数据成员 + 虚表指针(_vfptr)
虚表没有
RTTI 信息没有有(存储在虚表中)
typeid 求值时机编译时运行时
typeid 是否访问虚表
能否识别派生类型不能能(通过虚表查找)
sizeof 大小较小较大(多一个指针)

五、dynamic_cast 的内存访问过程

intmain(){Circlecircle(5.0,1);Shape*shape=&circle;// 向下转换:Shape* → Circle*Circle*circlePtr=dynamic_cast<Circle*>(shape);return0;}

内存访问流程

在 RTTI 中,type_info有以下作用:

  1. 类型判断:供dynamic_cast比较对象实际类型与目标类型,确定能否转换。
  2. 信息提供:通过name()函数返回类型名称,助于调试与日志记录。
  3. 类型比较:重载==!=操作符,支持运行时类型比较。

六、总结

类型特性虚表RTTI 信息typeid 行为dynamic_cast
无虚函数编译时确定不可用
有虚函数运行时查表可用

关键结论

  1. RTTI 通过虚表中的 RTTI 指针实现
  2. 只有多态类型(有虚函数的类)才支持运行时类型识别
  3. typeid对多态类型解引用时会访问虚表(有运行时开销)
  4. dynamic_cast依赖 RTTI 信息进行安全检查
http://www.jsqmd.com/news/1181437/

相关文章:

  • 2026哈尔滨道外区品牌首饰回收本地口碑榜:五大平台变现哪家强 - 奢侈品回收实体店
  • JavaScript性能优化实战:关键策略与工具链
  • 终极性能对比:AMD 4位量化Qwen2.5-VL-7B-Instruct vs 原始BF16模型
  • 工业风道测量误差大?告别单点取样,矩阵测风才是解决方案
  • 终极Wand增强工具:3种简单方案永久解锁游戏修改体验
  • 上线后最先崩的不是代码,是小团队盲目追求“全自动化”的幻觉
  • 上海西装定制价值洞察:维纳缇高性价比的六大核心支撑 - 西装爱好者
  • 大模型时代存储市场复盘 2024-2026:AI 训推爆发下的存储需求变革
  • 如何在gemma-4-e4b-it-OptiQ-4bit上实现推测解码:使用助手起草器提升推理速度的完整指南
  • 重新定义Palworld存档编辑:专业级开源工具实现游戏数据可视化与深度解析
  • AGNES算法实战:从距离度量到树状图可视化的完整指南
  • CBAM注意力机制:从结构图到PyTorch实战,逐行代码解析与性能调优
  • 静态联编与动态联编
  • 福州上门回收黄金须知!2026 年7月行情,正规商家交易注意事项 - 小蝶回收测评
  • Windows系统文件comuid.dll丢失找不到问题解决
  • GitHub上那些值得关注的RAG开源项目:从文档解析到多模态检索
  • 为什么你的AI Agent正在悄悄上传用户对话?——从TensorFlow Serving到LangChain SDK的6个默认风险配置
  • 热门公告:卡地亚复杂功能腕表维修标准指南 2026 线下服务地址汇总 - 卡地亚售后服务中心
  • 5分钟快速解锁Wand游戏修改器:免费增强方案终极指南
  • GPT-5.6、Claude、Gemini、DeepSeek 怎么选?按任务选型的终极对比清单
  • Codex 接入团队:Demo 跑得通,上线却崩盘?权限与日志才是生死线
  • 如何免费解锁Wand专业版:简单三步实现无限游戏体验终极指南
  • 很多人分不清火线、零线、地线,看完终于明白了
  • AMD Ryzen AI混合优化:Mistral-7B-Instruct-v0.3_rai_1.7.1_npu_4K的token后端配置
  • 集成学习之随机森林实战:从Bagging到分类预测
  • 2026年金华义乌电商侵权维权如何不踩雷?5家实测对比与源头快速处置指南 - 中国品牌企业观察网
  • Mask R-CNN 气球分割实战:从 VIA 标注到 PyTorch 训练,Loss 降至 0.02
  • 新手必看:AnyFlow-FAR-Wan2.1-14B-Diffusers常见问题与解决方案汇总
  • OpenDesign Internship项目全面解析:从基础架构到实践应用的完整指南
  • 高中生手机怎么选靠谱品牌?2026年5大核心维度横向对比分析 - 科技焦点