面试题答案
一键面试虚析构函数确保正确资源释放及多态行为的原理
- 多态行为:在C++中,当使用基类指针指向派生类对象(多态)时,通过基类指针调用虚函数,实际调用的是派生类中重写的版本。虚析构函数也遵循这一规则。如果析构函数不是虚函数,通过基类指针删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,导致派生类中动态分配的资源无法释放。
- 资源释放:当一个对象被删除时,其析构函数会被调用。对于包含动态分配资源(如使用
new
分配的内存)的类,析构函数负责释放这些资源。在类层次结构中,虚析构函数保证了从最派生类到基类的析构函数依次被调用。即先调用派生类的析构函数,释放派生类中动态分配的资源,然后再调用基类的析构函数,释放基类中的资源,从而确保所有动态分配的资源都被正确释放。
不当使用虚析构函数导致内存泄漏的场景
考虑以下代码示例:
class Base {
public:
Base() { data = new int; }
~Base() { delete data; } // 非虚析构函数
private:
int* data;
};
class Derived : public Base {
public:
Derived() { extraData = new char; }
~Derived() { delete extraData; }
private:
char* extraData;
};
int main() {
Base* ptr = new Derived();
delete ptr; // 这里只调用了Base的析构函数,Derived中的extraData未释放,导致内存泄漏
return 0;
}
在上述代码中,Base
类的析构函数不是虚函数。当通过Base
类指针ptr
删除Derived
类对象时,只有Base
类的析构函数被调用,Derived
类的析构函数没有被调用,Derived
类中动态分配的extraData
内存无法释放,从而导致内存泄漏。
避免内存泄漏的方法
将基类的析构函数声明为虚函数即可避免上述问题,修改后的代码如下:
class Base {
public:
Base() { data = new int; }
virtual ~Base() { delete data; } // 虚析构函数
private:
int* data;
};
class Derived : public Base {
public:
Derived() { extraData = new char; }
~Derived() { delete extraData; }
private:
char* extraData;
};
int main() {
Base* ptr = new Derived();
delete ptr; // 此时会先调用Derived的析构函数,再调用Base的析构函数,资源正确释放
return 0;
}
通过将Base
类的析构函数声明为虚函数,当通过Base
类指针删除Derived
类对象时,会先调用Derived
类的析构函数,释放extraData
,然后调用Base
类的析构函数,释放data
,从而避免内存泄漏。