基类指针转派生类指针必须用 dynamic_cast,因 static_cast 在类型不匹配时行为未定义;dynamic_cast 要求基类含虚函数(如 virtual ~base() = default),失败返回 nullptr(指针)或抛异常(引用),使用时须判空,不可忽略返回值。

基类指针转派生类指针必须用 dynamic_cast 而不是 static_cast
直接用 static_cast 强转基类指针到派生类指针,编译能过,但运行时行为未定义——只要该基类指针实际指向的不是对应派生类对象,就大概率崩溃或读写错误内存。而 dynamic_cast 会在运行时检查对象真实类型,安全得多。
但前提是:基类必须有虚函数(哪怕只是个空的虚析构函数),否则 dynamic_cast 编译失败,报错类似 error: cannot dynamic_cast ... (source type is not polymorphic)。
- 最简合规写法:在基类中加
virtual ~Base() = default; -
dynamic_cast对指针返回nullptr,对引用抛异常,日常建议只用于指针并判空 - 若已知类型绝对正确(如 CRTP 场景),可用
static_cast,但这是特例,不是通用解法
转换前必须确认指针确实指向派生类对象
常见错误是把一个纯基类对象的地址赋给基类指针,然后试图转成派生类指针。比如:Base b; Base* ptr = &b;,再 dynamic_cast<derived>(ptr)</derived> 必定返回 nullptr。
这种转换只在“基类指针原本就指向派生类对象”时才有意义,典型场景包括:
- 工厂函数返回
std::unique_ptr<base>,但你知道它内部构造的是Derived - 容器里存了
Base*,但插入时全都是new Derived - 回调参数是
Base*,而你作为调用方完全控制传入对象类型
如果不确定,不建议硬转;宁可重构接口,加虚函数暴露所需能力。
dynamic_cast 失败时不报错,但返回 nullptr 很容易被忽略
很多新手写了 dynamic_cast 却没检查返回值,直接解引用,结果段错误。这不是 dynamic_cast 的问题,是使用方式的问题。
正确姿势永远带判空:
if (Derived* d = dynamic_cast<derived>(base_ptr)) {
d->some_derived_only_method();
} else {
// 处理非 Derived 类型的情况,比如日志、默认行为或抛异常
}
</derived>
别用 reinterpret_cast 或 C 风格强制转换替代——它们绕过所有类型检查,比 static_cast 更危险。
多继承下 dynamic_cast 可能改变指针值
当派生类从多个基类继承时,不同基类子对象在内存中偏移不同。dynamic_cast 在跨继承路径转换时(比如从 Base1* 转到 Derived*,而 Derived 同时继承 Base1 和 Base2),会自动调整指针数值以对齐 Derived 起始地址。
这意味着:
-
dynamic_cast不是零开销操作,有运行时查虚表+偏移计算成本 - 不能假设转换前后指针值相等,尤其不要拿转换后的指针和原指针做
==比较 - 若需多次访问,应缓存转换结果,避免重复 cast
单继承下这个偏移为 0,但代码不该依赖此假设。
复杂点在于:类型安全不是靠语法“写对了”就成立的,而是靠整个对象生命周期内“谁创建、谁持有、谁转换”的上下文一致。最容易被忽略的是——以为加了虚函数就能随便 cast,却忘了检查指针来源是否真来自派生类实例。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











