vimacs's personal siteWritten with Vim and Emacs

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) 实际上是一个函数模板,通过反汇编也可以看出这点。这个函数模板使得编译器可以检查类的派生关系,并使得产生的共享指针对象中能保存其中裸指针的实际类型信息。