- 需要将析构函数声明为虚函数的情况:
- 当一个类可能会被继承,并且通过基类指针或引用删除派生类对象时,需要将基类的析构函数声明为虚函数。这样做是为了确保在通过基类指针删除派生类对象时,能够正确调用派生类的析构函数,避免内存泄漏等问题。
- 举例说明:
#include <iostream>
class Base {
public:
Base() { std::cout << "Base constructor" << std::endl; }
// 若不声明为虚析构函数,会有问题
virtual ~Base() { std::cout << "Base destructor" << std::endl; }
};
class Derived : public Base {
private:
int* data;
public:
Derived() {
data = new int(10);
std::cout << "Derived constructor" << std::endl;
}
~Derived() {
delete data;
std::cout << "Derived destructor" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
delete ptr;
return 0;
}
- 在上述代码中,如果
Base
类的析构函数不是虚函数,当执行delete ptr;
时,只会调用Base
类的析构函数,而不会调用Derived
类的析构函数,这样Derived
类中分配的data
内存就无法释放,导致内存泄漏。将Base
类的析构函数声明为虚函数后,通过Base
类指针删除Derived
类对象时,会先调用Derived
类的析构函数,再调用Base
类的析构函数,从而正确释放资源。
- 实现原理:
- 当一个类中有虚函数时,编译器会为该类生成一个虚函数表(vtable)。虚函数表是一个函数指针数组,存储了类中虚函数的地址。每个对象中都会包含一个指向虚函数表的指针(vptr)。
- 当通过基类指针或引用调用虚函数时,实际调用的函数是根据对象的实际类型(即vptr指向的虚函数表中的函数)来确定的。对于虚析构函数也是如此,在删除对象时,通过对象的vptr找到对应的虚析构函数并调用,从而保证能正确调用到派生类的析构函数,进而完成整个对象层次结构的清理工作。