我知道只要你有一个多态基类,基类就应该定义一个虚析构函数。因此,当删除派生类对象的基类指针时,它将首先调用派生类的析构函数。如果我在这里错了,请纠正我。
另外,如果基类析构函数是非虚拟的,则删除指向派生对象的基类指针将是未定义的行为。如果我错了,请纠正我。
所以我的问题是:为什么确切地说,当基类析构函数是非虚拟的时,对象不会被正确销毁?
我假设这是因为虚拟函数具有某种表,无论何时调用虚函数,它都会被记忆和查阅。并且编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数。我的假设是否正确?
答案 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
。