MST
星途 面试题库

面试题:C++子类析构与父类析构函数调用的内存管理考察

假设存在一个继承体系,父类有动态分配的资源,子类也有动态分配的资源。请详细说明在子类对象析构过程中,父类和子类的析构函数对这些资源的释放顺序以及内存管理要点,并给出相关代码示例。
45.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 资源释放顺序
    • 当子类对象被析构时,首先会调用子类的析构函数。在子类析构函数中,会先释放子类自己动态分配的资源。
    • 然后,父类的析构函数会被自动调用,用于释放父类中动态分配的资源。这是因为析构函数的调用顺序与构造函数相反,构造函数是先调用父类构造函数再调用子类构造函数,而析构函数是先调用子类析构函数再调用父类析构函数。
  2. 内存管理要点
    • 确保析构函数正确实现:在父类和子类的析构函数中,要正确地释放各自动态分配的资源,避免内存泄漏。对于使用new分配的内存,要使用delete释放;对于使用new[]分配的数组内存,要使用delete[]释放。
    • 父类析构函数应声明为虚函数:如果父类析构函数不是虚函数,当通过父类指针删除子类对象时,只会调用父类的析构函数,子类的析构函数不会被调用,从而导致子类动态分配的资源无法释放,产生内存泄漏。
  3. 代码示例
#include <iostream>
#include <cstring>

class Parent {
public:
    Parent(const char* name) {
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
    }
    // 声明为虚析构函数
    virtual ~Parent() {
        std::cout << "Parent destructor: releasing " << name << std::endl;
        delete[] name;
    }
private:
    char* name;
};

class Child : public Parent {
public:
    Child(const char* name, int id) : Parent(name) {
        this->id = new int;
        *this->id = id;
    }
    ~Child() {
        std::cout << "Child destructor: releasing id " << *id << std::endl;
        delete id;
    }
private:
    int* id;
};

int main() {
    Parent* p = new Child("John", 10);
    delete p;
    return 0;
}

在上述代码中:

  • Parent类动态分配了一个字符数组用于存储名字,并在其析构函数中释放该数组。
  • Child类继承自Parent类,动态分配了一个整数用于存储ID,并在其析构函数中释放该整数。
  • Parent类的析构函数声明为虚函数,确保通过父类指针删除子类对象时,子类的析构函数也能被调用,从而正确释放所有动态分配的资源。