多态基类应当声明virtual析构函数

当继承体系中涉及动态分配对象时,设置一个基类virtual析构函数就显得很必要了。

因为多态,我们在调用虚函数时根据绑定的真实类型选择合适的版本。调用非虚函数,则直接根据静态类型进行绑定,不能令动态类型和静态类型不一致(后面补充了一些细节)。
再回到这上面来,析构函数是non-virtual,但我们用的是一个基类指针指向一个派生类对象,那么调用时就会调用基类的析构函数,那么派生类部分的析构呢?
派生类部分并不会被析构,从而造成“局部销毁”现象,这很可能会导致内存泄漏以及给调试器添堵。
所以你要知道C++编译器有明确要求:

当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果是未定义的。

要完整的删除基类指针指向的动态分配的派生类对象,只要将基类的析构函数声明为virtual即可,此时就会动态绑定派生类对象的析构函数,并沿着继承体系隐式调用基类部分的析构函数,直至完整销毁。

不含虚函数的类不需要virtual析构函数

一个类如果不含虚函数,那么一般意味着它不是被用做一个基类。我们也一般不为其声明虚析构函数。在解释为什么如此之前,让我们了解下虚函数的实现细节。


实现虚函数,对象要携带某些信息以便在运行期绑定哪个版本。该对象的信息通常是由一个指针 vptr(virtual table pointer)指出的,它指向一个由函数指针构成的数组,我们称之为vtbl(virtual table),每一个带有虚函数的类都有对应的vtbl,当调用某一个虚函数时,取决于该对象的vptr指向的vtbl,编译器会在其中寻找合适的函数指针。


然并卵,实际上不需要虚析构函数主要是因为该类如果含有虚函数,会使得其对象的体积增大,因为该对象要存放vptr。这样原本能塞进的缓存器也不能如愿,同时也不能传递给其他语言(其他语言对象没有vptr),除非你明确补偿vptr。
既然不是作为基类而设计的,那么声明虚析构函数会是个很糟糕的想法,因此比较公认的准则是:

只有当class内含至少一个virtual函数,才为它声明virtual析构函数

不具备多态性的类也不声明virtual析构函数

多态性基类的设计目的是——为了使基类接口能处理派生类对象,有些类设计不是为了被用作基类,有些类虽然是基类但是不是用于多态用途,这些类都不需要声明virtual析构函数。