深入 C++ RTTI 与多态的底层原理及实践
C++ RTTI 的核心组成(typeid、dynamic_cast)、底层原理(vptr、vtable)及使用场景。RTTI 仅适用于多态类型,通过虚函数表承载类型信息。虽然支持安全向下转型和运行时查询,但存在空间和时间开销。建议优先使用虚函数实现多态,仅在必要时谨慎使用 RTTI,高性能场景可禁用。

C++ RTTI 的核心组成(typeid、dynamic_cast)、底层原理(vptr、vtable)及使用场景。RTTI 仅适用于多态类型,通过虚函数表承载类型信息。虽然支持安全向下转型和运行时查询,但存在空间和时间开销。建议优先使用虚函数实现多态,仅在必要时谨慎使用 RTTI,高性能场景可禁用。

本文详解 C++ RTTI 的核心组成(typeid、dynamic_cast)、底层原理(vptr、vtable)及使用场景,强调其仅适用于多态类型,并分析其开销与设计原则,指导合理应用。
C++ 的 RTTI(Run-Time Type Information,运行时类型信息) 是一套在程序运行时查询和操作对象类型信息的机制。它主要用于在继承体系中安全地识别和转换对象的实际类型。
C++ 标准库通过以下两个主要组件提供 RTTI 支持:
typeid 运算符const std::type_info& 对象。#include <iostream>
#include <typeinfo>
class Base {
virtual ~Base() = default;
}; // 必须有虚函数!
class Derived : public Base {};
int main() {
Base* b = new Derived();
std::cout << typeid(*b).name() << std::endl; // 输出实际类型(如 "7Derived")
std::cout << (typeid(*b) == typeid(Derived)) << std::endl; // true
delete b;
}
注意:只有多态类型(含虚函数的类)才能通过基类指针/引用正确识别派生类类型。否则
typeid返回的是静态类型(即指针声明的类型也就是对应的 Base)。
dynamic_cast 运算符Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b);
if (d) {
std::cout << "Cast succeeded!" << std::endl;
} else {
std::cout << "Cast failed!" << std::endl;
}
nullptrstd::bad_cast 异常同样要求:被转换的类型必须是多态类型(有虚函数),否则编译报错。
从编写完对应代码时,编译器处理转化方面讲起:
当一个类被定义为多态类型时,编译器会在编译期自动插入额外的数据结构和指针,主要包括:
关键点:现代主流编译器(如 GCC、Clang、MSVC)还会在 vtable 的开头或特定位置,存入一个指向 std::type_info 对象的指针;也就是说:vtable 不仅用于虚函数调用,也承载了类型信息。
内存中的最开始处,会隐式包含一个指针,称为 vptr(virtual table pointer);vptr 指向该对象所属类的 vtable。
例如:Base obj; // 内存布局:// [vptr] → 指向 Base 的 vtable// [其他成员变量...]
注意:这个 vptr 是编译器自动添加的,程序员不可见,但真实存在。
dynamic_cast 等的时候(多态时候),它可以判断是否能转换,进而做出对应应答!因此当我们运行时候就会存在这样一个结构:

场景 1:typeid(obj)Base* p = new Derived();
std::cout << typeid(*p).name();
执行过程如下:
std::type_info&。因此,即使 p 是 Base 类型,typeid(p) 也能知道它实际指向的是 Derived 对象!
注意:如果 Base 没有虚函数(非多态),则对象没有 vptr,编译器无法在运行时知道实际类型,此时 typeid(*p) 只能返回 Base(静态类型),且某些编译器甚至会直接报错或行为未定义。
dynamic_cast<Derived*> ( p )执行过程更复杂一些:
type_info(即实际类型 T);这也就是 RTTI 核心(运行时检查目标类型 Derived 是否是 T 的基类或派生类?):
此时就是拿着对应 type_info 类型按照对应的继承关系图去对比,如果在里面存着这个继承关系也就是在里面就进行转化,不在进行异常处理。
比如:若 p 实际指向 OtherClass,而 OtherClass 与 Derived 无继承关系,则 dynamic_cast 失败。
std::type_info 类简介name():返回类型的实现定义名称(通常是 mangled name,可用 c++filt 解码);operator== / operator!=:比较两个类型是否相同;before():用于 std::type_index 的排序(C++11)。如:
std::cout << typeid(int).name() << std::endl;
std::cout << typeid(std::string).name() << std::endl;

解码操作:

type_info 对象;dynamic_cast 需遍历继承树,比 static_cast 慢;许多高性能项目(如游戏引擎、嵌入式系统)会禁用 RTTI(编译选项
-fno-rtti)以节省资源。
| 场景 | 是否推荐 |
|---|---|
| 安全向下转型(无法用虚函数替代) | ✅ 谨慎使用 |
| 调试/日志打印类型名 | ✅ 可接受 |
| 序列化/反序列化框架 | ✅ 合理 |
| 日常业务逻辑中频繁类型判断 | ❌ 应重构为多态设计 |
在 GCC/Clang 中:
g++ -fno-rtti main.cpp
此时:
typeid 和 dynamic_cast 将无法使用,编译报错;没有虚函数;因此,运行时无法获取其实际类型信息。
| 特性 | 说明 |
|---|---|
| 作用 | 运行时获取类型信息、安全类型转换 |
| 前提 | 类必须是多态类型(有虚函数) |
| 核心工具 | typeid、dynamic_cast、std::type_info |
| 性能 | 有运行时开销,非零成本 |
| 最佳实践 | 优先用虚函数,RTTI 仅作兜底方案 |
善用多态,少用类型判断,才是面向对象的精髓。
RTTI 是 C++ 实现运行时类型识别的关键机制,依赖虚函数表与 type_info,在多态类型中支持安全转型和类型查询,但存在性能开销,应优先通过虚函数实现多态,谨慎使用 RTTI。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online