virtual destructor in polymorphic classes
我理解,只要您有一个多态的基类,这个基类就应该定义一个虚拟析构函数。因此,当删除指向派生类对象的基类指针时,它将首先调用派生类的析构函数。如果我错了,请纠正我。
此外,如果基类析构函数是非虚拟的,则删除指向派生对象的基类指针是未定义的行为。如果我错了也纠正我。
所以我的问题是:为什么当基类析构函数是非虚拟的时,对象不会被正确地销毁?
我假设这是因为虚函数有某种表,每当调用虚函数时,都会记住并查询该表。编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数。
我的假设正确吗?
如果在删除对象时,变量的静态类型是BAS类型,那么将调用基类型的析构函数,但不会调用子类的析构函数(因为它不是虚拟的)。
因此,基类分配的资源将被释放,而子类分配的资源将不会被释放。
这样就不会正确地破坏对象。
关于该表,您是正确的:它被称为虚拟方法表或"vtable"。但是,析构函数非虚拟化的结果并不是析构函数没有按正确的顺序调用,而是根本没有调用子类的析构函数!
考虑
1 2 3 4 5 6 7 8 | struct Base { void f() { printf("Base::f"); } }; struct Derived : Base { void f() { printf("Derived::f"); } }; Base* p = new Derived; p->f(); |
这将打印
1 2 3 4 5 6 7 8 | struct Base { ~Base() { printf("Base::~Base"); } }; struct Derived : Base { ~Derived() { printf("Derived::~Derived"); } }; Base* p = new Derived; p->~Base(); |
这打印了
1 2 3 4 5 6 7 8 | struct Base { virtual ~Base() { printf("Base::~Base"); } }; struct Derived : Base { ~Derived() override { printf("Derived::~Derived"); } }; Base* p = new Derived; p->~Base(); |
调用
1 2 | Derived::~Derived Base::~Base |
现在,一个删除表达式通常等价于一个析构函数调用,然后调用一个内存释放函数。在这种特殊情况下,表达式
1 | delete p; |
等于
1 2 | p->~Base(); ::operator delete(p); |
因此,如果析构函数是虚拟的,那么这是正确的:它首先调用