1. 问题背景与核心概念
在C++面向对象编程中,析构函数负责对象生命周期结束时的资源清理工作。当基类指针指向派生类对象时,如果基类析构函数非虚,通过基类指针删除对象可能导致派生类部分的资源泄漏。这个看似简单的语法规则背后,隐藏着C++对象模型和内存管理的核心机制。
我曾在实际项目中遇到过这样一个案例:一个图形编辑器基类Shape派生出Circle、Rectangle等子类,当通过Shape*指针数组批量删除图形对象时,部分子类对象的纹理资源未被正确释放。经过排查,最终发现根本原因就是基类析构函数没有声明为virtual。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 虚析构函数的工作原理
2.1 虚函数表机制
当类中包含虚函数时,编译器会为该类生成一个虚函数表(vtable),其中存储着该类所有虚函数的地址。每个对象内部则包含一个指向vtable的指针(vptr)。对于虚析构函数:
cpp复制class Base {
public:
virtual ~Base() { /*...*/ } // 虚析构函数
};
编译器会在vtable中为析构函数分配一个槽位,派生类会继承这个虚函数接口。通过基类指针调用析构函数时,实际执行的是vtable中记录的最终派生类的析构函数。
2.2 析构函数的调用链
完整的对象销毁过程包含三个关键步骤:
- 根据对象的动态类型调用最终派生类的析构函数
- 执行析构函数体中的代码
- 递归调用基类析构函数
当基类析构函数为虚函数时,这个调用链才能正确建立。以下是一个典型的多态删除场景:
cpp复制Base* obj = new Derived(); // 基类指针指向派生类对象
delete obj; // 正确调用Derived::~Derived()
3. 非虚析构函数的风险场景
3.1 资源泄漏的典型模式
考虑以下类层次结构:
cpp复制class Base {
public:
~Base() { cout << "Base destructor" << endl; }
};
class Derived : public Base {
int* resource;
public:
Derived() : resource(new int[10
