Appearance
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) | 返回 nullptr | p 多态;可行返回 T*,含“从兄弟虚基类之间/基类向多派生” |
dynamic_cast<T&>(r) | 抛 std::bad_cast | 引用无法表达空 → 抛异常 |
-fno-rtti | dynamic_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”是加分。
六、常见题
- 为什么 dynamic_cast 需要虚函数?—— 靠 vptr 上溯当前 vtable 找真实 type_info 才能判类型。
- 引用版失败抛异常而指针返回 null?—— 引用无“空”表达失败。
typeid(非多态)能否用于“运行期确认真实派生”?不能——非多态只有静态类型。- 关闭 RTTI 后果?—— typeid/dynamic_cast 不可用;可改用静态多态/访问者。
七、组织语言
「RTTI 利用多态对象 vptr 指向 vtable 时,表里带出实际类型 type_info;typeid 直接比类型,dynamic_cast 沿继承关系安全地转并校验。它需要 vtable(有虚函数);引用失败抛 bad_cast,指针失败给 null。性能敏感代码避免热路径用它,偏好编译期多态;库常 -fno-rtti -fno-exceptions 减开销与体积,同时禁掉这些特性。」