虚析构函数确保通过基类指针删除派生类对象时能正确调用派生类析构函数,从而完整释放资源;若未声明为virtual,则仅调用基类析构函数,导致派生类资源泄漏。

虚析构函数本身不改变销毁顺序,只确保调用链完整
虚析构函数不会调整析构执行顺序,它解决的是“该不该调用派生类析构函数”这个前提问题。如果基类析构函数不是 virtual,而你用 Base* 指针指向 Derived 对象并执行 delete,那么只有 Base::~Base() 会被调用,Derived::~Derived() 完全跳过——这不是顺序错了,是根本没触发。
一旦基类声明了 virtual ~Base(),后续销毁行为就回归标准顺序:派生类析构体 → 成员逆序析构 → 基类析构体。
多态删除时的完整析构流程(带虚析构)
假设你写了这样的代码:
class Base {
public:
virtual ~Base() { std::cout << "Base destroyed\n"; }
};
class Derived : public Base {
std::string s;
int* p;
public:
~Derived() { std::cout << "Derived destroyed\n"; }
};
// ...
Base* ptr = new Derived();
delete ptr; // 关键:通过基类指针 delete 派生类对象
此时实际发生的步骤是:
立即学习“C++免费学习笔记(深入)”;
-
Derived::~Derived()先执行(因为虚析构触发了动态绑定) - 然后按声明逆序析构成员:
s的析构(std::string自带)→p所指内存需手动delete(若未做则泄漏) - 最后调用
Base::~Base()
虚析构 + 继承 + 成员组合时的三层顺序
当类既有继承关系,又包含多个成员对象,且你通过多态方式销毁时,析构顺序严格分层,不能混淆:
- 第一层:派生类自己的析构函数体(
~Derived()) - 第二层:派生类中所有非静态成员对象,按其在类内声明的逆序析构(与初始化列表无关)
- 第三层:基类部分,包括基类自己的析构函数体,以及基类内部的成员(同样按基类内声明逆序)
注意:基类的成员析构发生在整个派生类析构流程的末端,不是和派生类成员混在一起的。常见错误是以为“所有成员一起逆序”,其实它们被继承层级隔开了。
不写 virtual ~Base() 会出什么错?
典型现象是程序看似正常退出,但资源没释放、日志没打印、文件没关闭,且无任何编译或运行时错误提示。比如:
- 派生类中
new的内存没被delete,导致泄漏 -
std::thread成员没被join()或detach(),析构时std::terminate() - 自定义句柄(如
FILE*、OpenGL texture ID)未清理,下次复用时失效
这种 bug 很难调试,因为崩溃点往往远离问题源头——它不是顺序错了,是压根没走到该走的地方。


















