C++ 虚析构函数的使用
不熟悉 C++ 的程序员会遇到一个问题,什么时候需要用虚析构函数? When to use virtual destructors? 解答了这个问题。
简单地说,就是如果需要通过基类指针析构对象的话,就需要虚析构函数,否则只有基类的析构函数会被调用,导致派生类中的使用的资源不会被释放。
那么什么时候会用到基类指针呢?通常是在使用多态类的时候。这就是为什么通常有虚函数的类需要有虚析构函数。但是在非多态类中,如果程序员要用基类指针调虚构函数的话,同样会有内存泄漏的问题,因此同样需要虚析构函数。例如:
#include <vector> class Base { public: virtual ~Base() {} }; class Derive: public Base { std::vector<int> v; public: Derive(): v(4, 0) {} }; int main() { Base *p = new Derive; delete p; }
如果 Base 的析构函数不是虚函数,就会导致 Derive 类中的 vector 发生内存泄漏,使用 address sanitizer 可以证实这点。当然了,ASAN 会报一个 new-delete-type-mismatch 错误,表明调用了不正确的析构函数,设置 ASAN_OPTIONS=new_delete_type_mismatch=0 运行程序,就会报出内存泄漏。
不过,对于非多态类,程序员一般不会通过基类指针释放一个对象,因此不需要定义析构函数为虚函数。
此外,Stack Overflow 中有一个回答很有意思,如果使用 shared_ptr<Base>(new Derive) 的话,那么共享指针释放资源的时候,调用的是派生类的析构函数。通过查看 shared_ptr 的实现,可以发现 shared_ptr<_Tp>(_Yp *p) 实际上是一个函数模板,通过反汇编也可以看出这点。这个函数模板使得编译器可以检查类的派生关系,并使得产生的共享指针对象中能保存其中裸指针的实际类型信息。