MST

星途 面试题库

面试题:C++ 虚析构函数与多态性及内存管理

假设有一个复杂的C++ 类层次结构,其中包含多个嵌套类和动态分配的资源。在使用多态指针删除对象时,详细阐述虚析构函数是如何确保正确的资源释放以及多态行为的。同时,举例说明不当使用虚析构函数可能导致的内存泄漏场景,并解释如何避免。
40.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚析构函数确保正确资源释放及多态行为的原理

  1. 多态行为:在C++中,当使用基类指针指向派生类对象(多态)时,通过基类指针调用虚函数,实际调用的是派生类中重写的版本。虚析构函数也遵循这一规则。如果析构函数不是虚函数,通过基类指针删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,导致派生类中动态分配的资源无法释放。
  2. 资源释放:当一个对象被删除时,其析构函数会被调用。对于包含动态分配资源(如使用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,从而避免内存泄漏。