typeid.name()每次不同因编译器实现差异:MSVC输出可读名如"class std::string",GCC/Clang默认输出mangled名如"Ss";应避免字符串比较,改用typeid(a)==typeid(b)或hash_code()判断相等。

typeid 拿到的类型名为什么每次都不一样?
因为 typeid 返回的 std::type_info::name() 是编译器实现定义的,MSVC 输出类似 "class std::string",GCC/Clang 默认输出 mangled 名(比如 "Ss"),根本没法直接读。别拿它做字符串比较或日志打印。
正确做法是用 typeid(a) == typeid(b) 做类型相等判断,或者用 typeid(a).hash_code() == typeid(b).hash_code()(C++11 起支持,跨编译器稳定)。
- 只在调试、断言里用
name(),且加注释说明“仅用于人工识别” - 生产代码中禁止用
std::string(typeid(x).name()) == "xxx"这类逻辑 - 如果真要可读名,自己维护一个
map<type_info const string></type_info>映射表,或用abi::__cxa_demangle(Linux)手动解码(注意内存管理)
dynamic_cast 为什么返回 nullptr 而不是抛异常?
dynamic_cast 对指针转型失败时返回 nullptr;对引用转型失败时才抛 std::bad_cast。很多人只记得“会失败”,却忘了指针和引用行为完全不同。
常见错误:把基类指针 Base* p 直接 dynamic_cast<derived>(*p)</derived>,一旦 p 实际不是 Derived,程序立刻崩溃——因为引用不能为 null,失败只能抛异常。
立即学习“C++免费学习笔记(深入)”;
- 优先用指针版本:
if (auto* d = dynamic_cast<derived>(p)) { ... }</derived> - 除非你明确需要异常语义,否则别碰引用版本
- 确保目标类有虚函数(至少一个),否则编译报错:
error: 'dynamic_cast' requires a pointer to complete class type
RTTI 被关掉后哪些东西会挂?
用 -fno-rtti(GCC/Clang)或 /GR-(MSVC)禁用 RTTI 后,typeid 和 dynamic_cast 全部失效:前者编译报错,后者直接退化成 static_cast(不检查、不安全)。
这不是“性能优化”的万能开关。很多第三方库(比如 Boost.TypeErasure、某些序列化框架)底层依赖 dynamic_cast,关掉 RTTI 会导致运行时行为错乱,且无编译警告。
- 只有在嵌入式等极端资源受限场景,且确认项目里 0 使用 RTTI 才考虑关闭
- 检查是否启用:
#ifdef __GXX_RTTI(GCC)或_CPPRTTI(MSVC) - 关了 RTTI 就别用
std::any、std::variant(部分实现依赖 RTTI)
替代 dynamic_cast 的更轻量方案有哪些?
如果只是想做“类型分发”,dynamic_cast 不是唯一解。虚函数 + 模板访客模式或 std::visit 更快、更可控,也避免 RTTI 开销。
例如多态对象处理:与其写一堆 if (auto* x = dynamic_cast<a>(p)) {...} else if (auto* y = dynamic_cast<b>(p)) {...}</b></a>,不如在基类加纯虚函数 virtual void accept(Visitor&) = 0;,让每个派生类实现具体 dispatch。
- 单层继承且类型固定,用枚举 +
switch配合static_cast(前提是能保证类型安全) - C++17 起,用
std::variant+std::visit替代继承树,完全绕过 RTTI - 注意:这些方案不解决“运行时未知类型”的问题(比如插件系统加载的类),此时 RTTI 仍是刚需
RTTI 不是黑魔法,它本质是编译器在虚表附近塞的一小块类型元数据。用不用,取决于你是否真的需要在运行时回答“这个指针背后到底是什么”——而不是为了看起来“更面向对象”。











