dynamic_cast安全判断派生类类型必须开启RTTI且基类为多态类型(含虚函数),否则编译报错;它通过虚函数表查询类型信息,指针转换失败返回nullptr,引用则抛std::bad_cast异常。

用 dynamic_cast 安全判断派生类类型
只有开启 RTTI(运行时类型信息)且基类有虚函数时,dynamic_cast 才能正常工作。它不是“猜”,而是查虚表里的类型信息——所以基类必须是多态的(至少有一个虚函数,通常是虚析构函数)。如果基类没虚函数,dynamic_cast 编译直接报错:error: cannot dynamic_cast ... (source type is not polymorphic)。
典型用法是把基类指针转成某个具体派生类指针,成功则返回非空指针,失败返回 nullptr:
Base* ptr = get_some_object();
DerivedA* a = dynamic_cast<DerivedA*>(ptr);
if (a) {
// 确实指向 DerivedA 实例
}
- 必须用指针或引用做
dynamic_cast,不能对对象本身强转(会触发拷贝/切片) - 转换失败时,指针结果为
nullptr,引用则抛std::bad_cast异常 - 编译时加
-fno-rtti会禁用 RTTI,此时dynamic_cast不可用
为什么不用 typeid 直接比名字
typeid(*ptr).name() 看起来简单,但名字是编译器实现相关的(比如 GCC 返回 St9basic_iosIcSt11char_traitsIcEE 这种乱码),不可靠也不可移植。更关键的是,typeid 对于带 cv 限定符或引用类型的表达式行为微妙,容易误判。
真正安全的做法是用 typeid 比较类型对象本身:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
if (typeid(*ptr) == typeid(DerivedB)) {
// 成立说明 *ptr 动态类型就是 DerivedB(不是其派生类)
}
- 这个判断只认“完全匹配”,
DerivedC继承自DerivedB的话,typeid(*ptr) == typeid(DerivedB)仍为 false - 必须解引用
ptr(即*ptr),传指针本身会得到typeid(Base*),不是你想要的动态类型 - 和
dynamic_cast一样,要求基类是多态的,否则typeid(*ptr)返回的是静态类型Base
避免用虚函数“打补丁”模拟类型判断
有人会在基类加个纯虚函数 virtual const char* type_name() const = 0;,让每个派生类返回字符串。这看似灵活,但隐患明显:
- 字符串比较慢,且易拼错、大小写敏感、不支持 IDE 跳转或编译期检查
- 无法用于模板分发(比如根据类型调不同特化函数)
- 若派生类忘记重写,运行时报纯虚函数调用错误,而不是编译报错
- 新增派生类时,所有 switch-type-name 的地方都要手动更新,违背开闭原则
这类设计本质上是把类型系统降级为字符串系统,失去 C++ 类型安全的优势。
性能与调试注意事项
dynamic_cast 不是零成本:它要遍历虚表中的类型信息链,深度继承体系下可能有微小开销。但绝大多数场景下,这点开销远小于逻辑错误带来的代价。真有性能瓶颈时,应先 profile 确认是不是这里拖慢了,而不是提前规避。
调试时注意:GDB/Lldb 中 print *ptr 通常只显示基类部分;想看真实类型,用 print typeid(*ptr).name() 或直接 dynamic_cast 尝试转成各派生类指针再打印。
最常被忽略的一点:基类析构函数没声明为 virtual 时,哪怕其他虚函数都齐全,RTTI 也可能在某些编译器上行为异常——务必确保有虚析构函数,哪怕它是空的。

















