【为什么析构函数要虚函数】
承接上文我们已经了解了虚表,虚指针以及虚函数。
前提条件
首先不是 所有析构函数都要设置成虚的。是否将父类的析构函数定义为虚函数取决于该父类是否会被用作多态基类(即是否会通过基类指针或引用删除派生类对象)。抽象基类必须将析构函数定义为虚函数(即使它是纯虚的)。
为什么析构函数要是虚函数
确保对象完全析构,避免调用不到子类的析构函数。在多态的场景下,当使用基类指针指向派生类对象时,如果基类的析构函数不是虚函数,那么在通过基类指针删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类中动态分配的资源(如动态数组、文件句柄等)无法被正确释放,从而引发内存泄漏或其他资源管理问题当基类的析构函数是虚函数时,派生类的析构函数会自动与基类的析构函数构成重写(尽管名字不同,编译器会做特殊处理),这样在删除对象时,就能根据对象的实际类型调用正确的析构函数,实现多态性在对象销毁阶段的完整表现。
举个例子来看看这个问题
A是基类,B是派生类
int main()
{
A*ptr=new B();
delete ptr;
return 0;
}
1. 若~A不是虚函数:
此时,编译器仅根据指针类型(`A`)决定调用的析构函数,而不考虑对象的实际类型(`B`)。子类 `B` 的析构函数未被触发,导致 `B` 中分配的资源(如 `data` 数组)无法释放。
2. 若~A是虚函数:
运行时通过对象的虚表指针(`vptr`)定位到 `B` 的虚表,从中获取 `B::~B()` 的地址并调用。子类析构函数执行后,编译器会**自动调用基类析构函数**(无论基类析构是否为虚),确保完整的反向构造顺序。