最安全的运行期类型比较是 typeid(a) == typeid(b);插件跨模块需用 hash_code() 或字符串 id,禁用 name() strcmp 和 type_info 指针存储。

std::type_info::name() 返回的字符串不可靠,别直接 strcmp
它不保证跨编译器、跨构建一致,甚至同一编译器在不同优化等级下都可能返回不同字符串(比如 std::string 可能是 Ss 或 std::string)。用 strcmp 比对 typeid(T).name() 的结果,在 CI 或 Release 构建里大概率崩。
- 真正安全的比较方式只有
typeid(A) == typeid(B)—— 这是标准保证的 O(1) 操作 - 如果需要可读名用于日志,只在调试时调用
abi::__cxa_demangle(GCC/Clang),且必须检查返回值和内存分配失败 - Windows MSVC 用
__unDName,但同样不能用于逻辑分支判断
插件注册时必须用 static_cast 而非 dynamic_cast 触发类型擦除
想让插件工厂返回统一接口指针(如 IPlugin*),又保留原始类型信息,常见错误是写成 dynamic_cast<iplugin>(new MyPlugin)</iplugin> —— 这会强制要求 IPlugin 有虚函数表,且运行期开销大,还依赖 RTTI 全局开启。
- 正确做法:插件基类
IPlugin不需要虚函数;注册时用static_cast<void>(new MyPlugin)</void>存原始指针,同时缓存&typeid(MyPlugin) - 调用方取回后,先比对
stored_typeid == typeid(T),再用static_cast<t>(ptr)</t>强转 —— 零虚函数开销,类型安全由程序员保证 - 注意:
typeid表达式必须作用于完整类型,不能是void*或未定义类型
std::type_info 对象本身不能跨 DLL/so 边界直接比较
在 Windows 动态库或 Linux .so 中,即使两个模块编译自同一份头文件,typeid(MyPlugin) 在主程序和插件中产生的 std::type_info 实例地址不同,== 比较会返回 false。
- 根本原因是各模块维护独立的 type_info 表,RTTI 信息不共享
- 解决方案:插件导出一个唯一字符串 ID(如
"com.example.myplugin.v1"),注册时存为std::string,比对用字符串而非typeid - 若坚持用
typeid,需确保所有插件与主程序链接同一份 STL(如全静态链接 libstdc++ 或 libc++),但这在实践中极难管控
std::type_info 的生命周期很短,别保存指针
typeid(T) 返回的是临时对象,其地址在表达式结束后就失效。下面这段代码是未定义行为:
立即学习“C++免费学习笔记(深入)”;
const std::type_info* ti = &typeid(MyPlugin); // 危险! register_plugin(ptr, ti); // ti 指向已销毁对象
- 正确做法:用
std::type_info const&接收并立即复制其内部状态?不行 ——std::type_info禁止拷贝 - 实际可行方案:调用
typeid(T).hash_code()(C++11 起),它返回size_t,可安全存储和比较 - 注意:
hash_code()不保证全局唯一,但同类型必相同,不同类型大概率不同;用于插件检查足够,无需强唯一性
hash_code 是目前最轻量、最跨平台、最不易踩坑的运行期类型标识手段。其他所有“取地址”“存 name 字符串”“跨模块比 typeid 对象”的做法,都在生产环境里栽过跟头。










