C++中的RTTI底层机制是什么?(typeid是如何工作的)

2次阅读

typeid通过编译器静态写入.rodata段的type_info对象获取类型名,其name()返回mangled字符串,需demangle才可读;比较用地址而非字符串;对多态类型依赖vtable,非多态类型则纯编译期处理。

C++中的RTTI底层机制是什么?(typeid是如何工作的)

typeid 是怎么拿到类型名字的?

它不靠运行时解析符号表,也不靠反射元数据——c++ 没有内置反射。typeid 的结果来自编译器在生成目标文件时**静态写入的只读数据段(.rodata 或类似节)**,每个具名类型(包括类、枚举、甚至 int* 这样的复合类型)都对应一个全局唯一的 type_info 对象实例。

这个实例里最关键的成员是 __name(GCC/Clang 下)或类似命名的私有指针,指向一段以 NULL 结尾的字符串字面量,内容就是该类型的“mangled name”(例如 _ZTSNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE)。你调用 typeid(obj).name() 得到的,就是这个 raw 字符串——它不保证可读,也不跨编译器一致。

  • 要看到可读名,得用 abi::__cxa_demangle(GCC/Clang)或 UnDecorateSymbolName(MSVC)手动解码
  • type_info::operator== 比较的是地址(同一类型的所有 typeid 返回引用指向同一个 type_info 实例),不是字符串内容比对
  • 如果类型不含虚函数(即无 vtable),typeid 仍有效,但此时它纯靠编译期生成的静态信息,和对象内存布局无关

为什么 typeid(*ptr) 有时崩溃,而 typeid(ptr) 没事?

因为 typeid 对表达式求值的规则不同:typeid(ptr) 只看指针类型Base*),不访问内存;而 typeid(*ptr) 要求 ptr 是有效的、指向完整对象的指针——否则触发未定义行为,常见于空指针、悬垂指针或指向未构造内存。

  • ptrnullptrtypeid(*ptr)std::bad_typeid 异常(仅当 ptr 是多态类型时;非多态类型直接 UB)
  • 多态类型 = 含虚函数的类,此时 typeid 会通过 vtable 查找对应的 type_info*(vtable 开头通常存有指向 type_info 的指针)
  • 非多态类型(如 Struct A { int x; };)的 typeid 完全不碰对象内存,只依赖编译期类型信息

dynamic_cast 和 typeid 共享同一套 RTTI 数据吗?

是,但用法隔离。它们都依赖编译器为多态类生成的 RTTI 数据块,该数据块通常紧邻 vtable 存储,包含:type_info*继承关系图、虚基类偏移等。但 dynamic_cast 用的是整个继承图做安全向下转型校验,而 typeid 只取其中的 type_info* 部分。

立即学习C++免费学习笔记(深入)”;

  • 关闭 RTTI(如 GCC 的 -fno-rtti)会让 dynamic_cast 编译失败(除非转成 void*),也会让 typeid 对多态类型失效
  • 关闭后,非多态类型的 typeid 仍可用——因为它根本没用到运行时数据
  • RTTI 数据增加二进制体积,且每次 dynamic_casttypeid 都有轻微间接寻址开销(查 vtable → 查 type_info)

哪些情况会让 typeid 返回结果“不可靠”?

不是不可靠,而是语义受限:它返回的是“静态表达式类型”或“动态最派生类型”,但无法穿透某些边界。

  • 对数组退化指针(int arr[5]; typeid(arr).name() 返回 int [5];但 int* p = arr; typeid(p).name() 返回 int* —— 丢掉了长度信息)
  • 对模板实例化类型,typeid(T) 中的 T 必须是具体类型;不能是未推导的 auto 或依赖型名称(SFINAE 场景下需谨慎)
  • 跨 DLL/so 边界时,若两个模块用不同编译器或不同 STL 实现,type_info 对象地址可能不等价,== 判断失效(即使名字相同)
  • 使用 std::anystd::variant 时,typeid 能告诉你当前存储的类型,但无法告诉你 std::any 内部是否做了类型擦除重包装

真正容易被忽略的是:RTTI 不提供字段名、成员函数签名或继承链遍历能力——它只回答“这是哪个类型”,不回答“它长什么样”。想做序列化或调试打印,光靠 typeid 远不够。

text=ZqhQzanResources