MST
星途 面试题库

面试题:C++ 继承中动态内存分配的常见问题

在C++ 继承体系中,当基类和派生类都有动态分配的成员变量时,描述一下可能会出现哪些内存管理方面的问题,如何通过合理地定义析构函数、拷贝构造函数和赋值运算符重载来避免这些问题?
41.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

可能出现的内存管理问题

  1. 内存泄漏
    • 如果派生类对象被销毁时,没有正确调用基类的析构函数,基类中动态分配的成员变量所占用的内存将不会被释放,从而导致内存泄漏。例如,基类Base有一个动态分配的int*成员变量data,如果派生类Derived的析构函数没有调用Base的析构函数,data指向的内存就无法释放。
    • 同样,如果派生类中动态分配的成员变量在析构函数中没有被正确释放,也会造成内存泄漏。
  2. 悬空指针
    • 当通过派生类对象的拷贝构造函数或赋值运算符重载创建新对象时,如果没有正确处理动态分配的成员变量,可能会导致多个对象指向同一块内存。当其中一个对象销毁时,这块内存被释放,其他对象中的指针就变成了悬空指针,再次使用这些悬空指针会导致未定义行为。

通过定义合理的函数避免问题

  1. 析构函数
    • 基类
      • 基类的析构函数应该声明为虚函数。这样,当通过基类指针删除派生类对象时,会调用派生类正确的析构函数,进而调用基类的析构函数,确保基类和派生类中动态分配的成员变量都能被释放。例如:
class Base {
public:
    int* data;
    Base() {
        data = new int(0);
    }
    virtual ~Base() {
        delete data;
    }
};
  • 派生类
    • 派生类的析构函数会自动调用基类的析构函数(如果基类析构函数是虚函数)。派生类在其析构函数中负责释放自己动态分配的成员变量。例如:
class Derived : public Base {
public:
    int* moreData;
    Derived() {
        moreData = new int(1);
    }
    ~Derived() {
        delete moreData;
    }
};
  1. 拷贝构造函数
    • 基类
      • 基类的拷贝构造函数应该对动态分配的成员变量进行深拷贝。即创建新的内存空间,并将源对象中动态分配成员变量的值复制到新的内存空间中。例如:
class Base {
public:
    int* data;
    Base() {
        data = new int(0);
    }
    Base(const Base& other) {
        data = new int(*other.data);
    }
    virtual ~Base() {
        delete data;
    }
};
  • 派生类
    • 派生类的拷贝构造函数首先要调用基类的拷贝构造函数来初始化基类部分,然后对派生类自己动态分配的成员变量进行深拷贝。例如:
class Derived : public Base {
public:
    int* moreData;
    Derived() {
        moreData = new int(1);
    }
    Derived(const Derived& other) : Base(other) {
        moreData = new int(*other.moreData);
    }
    ~Derived() {
        delete moreData;
    }
};
  1. 赋值运算符重载
    • 基类
      • 基类的赋值运算符重载函数要处理自赋值情况,并对动态分配的成员变量进行深拷贝。先释放自身已有的动态分配内存,然后重新分配并复制源对象的值。例如:
class Base {
public:
    int* data;
    Base() {
        data = new int(0);
    }
    Base(const Base& other) {
        data = new int(*other.data);
    }
    Base& operator=(const Base& other) {
        if (this == &other) {
            return *this;
        }
        delete data;
        data = new int(*other.data);
        return *this;
    }
    virtual ~Base() {
        delete data;
    }
};
  • 派生类
    • 派生类的赋值运算符重载函数首先调用基类的赋值运算符重载函数来处理基类部分,然后处理派生类自己动态分配的成员变量。同样要处理自赋值情况。例如:
class Derived : public Base {
public:
    int* moreData;
    Derived() {
        moreData = new int(1);
    }
    Derived(const Derived& other) : Base(other) {
        moreData = new int(*other.moreData);
    }
    Derived& operator=(const Derived& other) {
        if (this == &other) {
            return *this;
        }
        Base::operator=(other);
        delete moreData;
        moreData = new int(*other.moreData);
        return *this;
    }
    ~Derived() {
        delete moreData;
    }
};