考虑一下
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
。