多态类中的虚析构函数

时间:2015-03-31 08:17:04

标签: c++ inheritance polymorphism

我知道只要你有一个多态基类,基类就应该定义一个虚析构函数。因此,当删除派生类对象的基类指针时,它将首先调用派生类的析构函数。如果我在这里错了,请纠正我。

另外,如果基类析构函数是非虚拟的,则删除指向派生对象的基类指针将是未定义的行为。如果我错了,请纠正我。

所以我的问题是:为什么确切地说,当基类析构函数是非虚拟的时,对象不会被正确销毁?

我假设这是因为虚拟函数具有某种表,无论何时调用虚函数,它都会被记忆和查阅。并且编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数。

我的假设是否正确?

2 个答案:

答案 0 :(得分:7)

如果在删除对象时,变量的静态类型是bas类型,则将调用基类型的析构函数,但子类的析构函数不会被调用(如它不是虚拟的。)

因此,将释放基类分配的资源,但子类分配的资源不会被激活。

因此,对象不会被正确破坏。

你对那个表是正确的:它被称为虚拟方法表或" vtable"。但析构函数是非虚拟的结果并不是没有以正确的顺序调用析构函数,而是根本没有调用子类的析构函数!

答案 1 :(得分:2)

考虑

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。现在,如果我们将析构函数设置为virtual,那么,与任何其他虚函数一样,将调用对象的动态类型中的最终覆盖。析构函数会覆盖基类中的虚析构函数(即使它的“名称”不同):

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