MST

星途 面试题库

面试题:C++ 复杂场景下 delete 与 delete[] 对内存布局及析构函数调用的影响

考虑如下复杂场景:定义一个继承体系,基类 `Base` 有虚析构函数,派生类 `Derived` 有额外的动态分配资源。有一个 `Base** arr = new Base*[2]; arr[0] = new Base(); arr[1] = new Derived();`。 1. 如果使用 `delete arr[0];` 和 `delete[] arr;` 分别会对内存释放和析构函数调用产生什么影响? 2. 如何正确地释放 `arr` 所指向的内存,确保所有资源都被正确清理?请详细阐述内存布局、虚函数表以及析构函数调用顺序等方面在这个过程中的作用。
40.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
    • delete arr[0];
      • 内存释放arr[0] 指向 Base 类型的对象,delete arr[0]; 会释放 arr[0] 所指向的 Base 对象的内存。由于 Base 有虚析构函数,析构函数会被正确调用。
      • 析构函数调用Base 的虚析构函数会被调用,因为 Base 类定义了虚析构函数,这确保了在通过基类指针删除对象时,能正确调用到 Base 类的析构函数,从而正确清理 Base 对象的资源。
    • delete[] arr;
      • 内存释放delete[] arr; 试图释放 arr 所指向的数组内存。但是,数组中的元素是 Base* 类型的指针,这个操作并不会直接释放 arr[0]arr[1] 所指向的对象的内存。这会导致内存泄漏,因为 arr[0]arr[1] 所指向的对象的内存没有被释放。
      • 析构函数调用:由于 delete[] arr; 没有直接释放 arr[0]arr[1] 所指向的对象的内存,所以不会调用 BaseDerived 的析构函数,进一步加剧了内存泄漏问题。
  1. 正确释放内存的方法

    • 代码实现
#include <iostream>

class Base {
public:
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() {
        data = new int(0);
    }
    ~Derived() {
        std::cout << "Derived destructor" << std::endl;
        delete data;
    }
};

int main() {
    Base** arr = new Base*[2];
    arr[0] = new Base();
    arr[1] = new Derived();
    for (int i = 0; i < 2; ++i) {
        delete arr[i];
    }
    delete[] arr;
    return 0;
}
  • 内存布局
    • arr 是一个指向 Base* 类型数组的指针。数组中有两个元素,arr[0] 指向 Base 对象,arr[1] 指向 Derived 对象。Base 对象和 Derived 对象在内存中是独立分配的。Derived 对象的内存布局首先是 Base 部分(继承自 Base 类),然后是 Derived 类自己额外的数据成员(如 int* data)。
  • 虚函数表
    • Base 类有虚析构函数,这意味着 Base 类有一个虚函数表(vtable)。Derived 类继承自 Base 类,它也有自己的虚函数表,并且在 Derived 的虚函数表中,虚析构函数的指针指向 Derived 类的析构函数。当通过 Base* 指针调用虚析构函数时,程序会根据对象实际的类型(通过虚函数表指针找到对应的虚函数表)来调用正确的析构函数。
  • 析构函数调用顺序
    • delete arr[0]; 时,由于 arr[0] 指向 Base 对象,Base 的虚析构函数被调用。
    • delete arr[1]; 时,因为 arr[1] 指向 Derived 对象,通过虚函数表,Derived 的析构函数首先被调用。在 Derived 的析构函数执行完毕后,Base 的析构函数会被调用,因为 Derived 类继承自 Base 类,Base 类的析构函数会负责清理 Base 部分的资源。最后,delete[] arr; 释放 arr 所指向的数组内存,至此所有资源都被正确清理。