Skip to content

RTTI:typeid / type_info / dynamic_cast 运行时类型识别

4-cast 总表在 fundamental/macro-cast;本篇专门展开 C++ 运行时类型识别(RTTI):typeid/type_info 怎么拿类型、dynamic_cast 为何要 vtable、跨层级/兄弟转换、以及 HPC 为什么常 -fno-rtti

一、RTTI 是什么 & 两个入口

RTTI = Runtime Type Identification,运行时获知对象的动态类型。主要在未求值/需要动态多态处用:

  • typeid(expr):返回 const std::type_info&.name()/比较 ==判断类型。
  • dynamic_cast<T>(ptr/ref):安全下转型 / 兄弟间转换。

要求:进行这些判断的多态对象必须含虚函数(有 vptr);对非多态类型 typeid 只能取静态类型,dynamic_cast 非多态直接编译错。

cpp
struct Base { virtual ~Base(){} };   // RTTI 需要多态(vptr→可查真实类型)
struct D1 : Base {}; struct D2 : Base { void only(){} };

Base* b = getSomeObject();
if (D1* d = dynamic_cast<D1*>(b)) { /* 确认是 D1 */ }
if (typeid(*b) == typeid(D2)) { /* D2 */ }

二、底层机制(它怎么“知道”动态类型)

  • 对象的 vptr 指向 vtable,vtable 通常含指向 type_info 的入口(位置实现相关,Itanium/类目共通思路)。
  • dynamic_cast/typeid 顺着目前 vptr 指到的那份 vtable → 取该实际类型的 type_info 与目标比较(或沿继承关系判断是否可转)。
  • 因此要求多态(有 vptr);无虚函数的类不做 dynamic_cast(无“运行期当前类型”可查)。

type_info:标准提供 .name()(实现决定,gcc=Itanium mangled 名,可用 c++filt/abi::__cxa_demangle 还原)、==/!=.before()。虚基类/多态比较跨编译器不保证唯一(同 ABI 内可)。

三、dynamic_cast 语义速填

形式失败要求
dynamic_cast<T*>(p)返回 nullptrp 多态;可行返回 T*,含“从兄弟虚基类之间/基类向多派生”
dynamic_cast<T&>(r)std::bad_cast引用无法表达空 → 抛异常
-fno-rttidynamic_cast/typeid 不可用见下

vs static_cast 下转static_cast<D*>(b) 不做运行期校验(编译期信任,若是错误的派生就 UB);dynamic_cast 运行期校验(先经 RTTI 确认)→ 更安全但有开销。

四、什么时候“不该依赖 dynamic_cast”(面向对象反模式 + 性能)

设计层面高频追问:

  • 频繁 dynamic_cast 说明基类接口不完整/类型分支侵入——倾向改用多态虚函数/访问者,避免“类型切换”。
  • HPC/内核里 typeid/dynamic_cast 有运行期开销(查 RTTI/类型遍历),热点禁止。

五、HPC 常关 RTTI(-fno-rtti)的道理

  • 省去每个多态对象 type_info / vtable 中 RTTI 槽的少量空间与异常/动态转换路径开销。
  • 代价:不能用 typeid/dynamic_cast、异常某些路径受限;对“全部虚调用都在编译期定好、用 CRTP/模板分派”的代码库很干净。
  • 面试切 HPC:能讲“我倾向编译期多态(CRTP/templates);若必须运行时多态用虚函数基类分派而非动态_cast;可-fno-rtti + -fno-exceptions 减 bloat”是加分。

六、常见题

  1. 为什么 dynamic_cast 需要虚函数?—— 靠 vptr 上溯当前 vtable 找真实 type_info 才能判类型。
  2. 引用版失败抛异常而指针返回 null?—— 引用无“空”表达失败。
  3. typeid(非多态) 能否用于“运行期确认真实派生”?不能——非多态只有静态类型。
  4. 关闭 RTTI 后果?—— typeid/dynamic_cast 不可用;可改用静态多态/访问者。

七、组织语言

「RTTI 利用多态对象 vptr 指向 vtable 时,表里带出实际类型 type_info;typeid 直接比类型,dynamic_cast 沿继承关系安全地转并校验。它需要 vtable(有虚函数);引用失败抛 bad_cast,指针失败给 null。性能敏感代码避免热路径用它,偏好编译期多态;库常 -fno-rtti -fno-exceptions 减开销与体积,同时禁掉这些特性。」

C++ 面试八股 · VitePress 版