考虑
struct Base {
void f() { printf("Base::f"); }
};
struct Derived : Base {
void f() { printf("Derived::f"); }
};
Base* p = new Derived;
p->f();
这会打印出Base::f,因为Base::f 不是虚拟的。现在对析构函数做同样的事情:
struct Base {
~Base() { printf("Base::~Base"); }
};
struct Derived : Base {
~Derived() { printf("Derived::~Derived"); }
};
Base* p = new Derived;
p->~Base();
这打印出Base::~Base。现在,如果我们将析构函数设为虚拟,那么与任何其他虚拟函数一样,对象的动态类型中的最终覆盖器将被调用。析构函数会覆盖基类中的虚拟析构函数(即使它的“名称”不同):
struct Base {
virtual ~Base() { printf("Base::~Base"); }
};
struct Derived : Base {
~Derived() override { printf("Derived::~Derived"); }
};
Base* p = new Derived;
p->~Base();
调用p->~Base() 实际上调用Derived::~Derived()。由于这是一个析构函数,在其主体执行完毕后,它会自动调用基类和成员的析构函数。所以输出是
Derived::~Derived
Base::~Base
现在,delete-expression 通常相当于调用析构函数,然后调用内存释放函数。在这种特殊情况下,表达式
delete p;
等价于
p->~Base();
::operator delete(p);
因此,如果析构函数是虚拟的,则这样做是正确的:它首先调用Derived::~Derived,然后在完成后自动调用Base::~Base。如果析构函数不是虚拟的,则可能的结果是只调用了Base::~Base。