pure virtual method called 错误源于对象析构期间或释放后仍调用虚函数,导致 vtable 指向纯虚函数桩;最常见于基类析构函数中调用虚函数、析构时触发回调、多线程下 use-after-free 或裸指针竞态访问。

为什么会出现 pure virtual method called 错误
这是 C++ 运行时崩溃的典型信号,不是编译错误,说明程序已经跑起来了,但在某个时刻调用了纯虚函数。根本原因是:**对象的动态类型本应提供该虚函数的具体实现,但实际调用时,虚函数表(vtable)指向的是基类的纯虚函数桩(abort 或 __cxa_pure_virtual)**。最常见于对象生命周期管理失控——尤其是析构过程中。
典型触发路径是:派生类对象开始析构 → 先调用派生类析构函数 → 再自动调用基类析构函数 → 此时派生类部分已析构完毕,但若基类析构函数里又间接调用了虚函数(比如通过 this 指针、成员函数指针、或被观察者通知机制),就会命中尚未重写的纯虚函数入口。
检查析构顺序和虚函数调用链
重点排查所有在析构函数(包括基类和派生类)中出现的虚函数调用,哪怕看起来“安全”:
- 不要在基类析构函数里调用任何虚函数——哪怕你确信子类重写了它;此时子类成员已析构,
this不再指向有效派生对象 - 检查是否在析构函数中触发了回调、信号、事件分发(如 Qt 的
QObject::destroyed()信号槽、自定义 observer 通知);这些回调若持有this并尝试调用虚函数,极易出问题 - 确认所有虚函数调用都发生在对象完全构造之后、且未进入析构阶段;可在关键虚函数开头加断点或日志,打印
this和typeid(*this).name()辅助判断
识别多线程下的竞态析构
如果对象被多个线程共享,且存在“释放后使用”(use-after-free),也会导致该错误。例如:
- 线程 A 正在执行
delete obj,同时线程 B 还在通过裸指针或引用调用obj->virtual_func() - 智能指针(如
std::shared_ptr)的控制块被多线程并发访问,但析构时机与虚函数调用时机错位 - 没有对共享对象的生命周期做同步保护(比如缺少
std::shared_ptr或std::weak_ptr配合,或未用互斥锁保护原始指针访问)
建议统一用 std::shared_ptr 管理多线程共享对象,并在回调中捕获 weak_ptr,调用前先 lock();避免裸指针跨线程传递。
用 AddressSanitizer 和 UBSan 快速定位
单纯靠日志或断点很难抓到瞬时状态,推荐直接启用编译器检测工具:
- Clang/GCC 下加编译选项:
-fsanitize=address,undefined -fno-omit-frame-pointer,运行时会精确报出虚函数调用发生在哪一行、对象内存是否已被释放 - 特别注意 UBSan 输出中的
pure virtual method called附带的栈回溯——它通常能指出最后调用虚函数的位置,而非崩溃点本身 - 若用 GDB,可在
__cxa_pure_virtual处下断点:break __cxa_pure_virtual,然后run,看谁触发了它
这个错误从来不是“虚函数写错了”,而是“对象不该活的时候还在被调用”。真正难的不是修复,是发现谁在析构后还攥着那个指针。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











