MST

星途 面试题库

面试题:C++子类析构调用父类析构在内存管理及异常处理中的考量

在一个C++项目中,有一个包含动态内存分配的父类,子类继承自该父类并也进行了动态内存分配。请阐述在子类析构函数中正确调用父类析构函数,同时处理可能出现的内存泄漏和异常情况的策略。并以代码示例展示如何优雅地处理这种复杂场景,包括RAII机制的应用。
42.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

策略阐述

  1. 调用父类析构函数:在子类析构函数中,父类析构函数会被自动调用,无需显式调用。但如果父类析构函数不是虚函数,可能会导致析构不完整,所以父类析构函数应声明为虚函数。
  2. 处理内存泄漏:使用RAII(Resource Acquisition Is Initialization)机制,将动态分配的资源(如内存)封装在对象中,利用对象的生命周期来自动管理资源的释放。这样在对象销毁时,其析构函数会自动释放资源,避免内存泄漏。
  3. 处理异常情况:在构造函数和其他可能抛出异常的函数中,确保在异常抛出前已正确释放所有已分配的资源。使用try - catch块捕获异常,并在catch块中进行资源清理。

代码示例

#include <iostream>
#include <memory>

class Parent {
public:
    Parent() {
        data = new int[10];
        std::cout << "Parent constructor" << std::endl;
    }
    virtual ~Parent() {
        delete[] data;
        std::cout << "Parent destructor" << std::endl;
    }
private:
    int* data;
};

class Child : public Parent {
public:
    Child() {
        try {
            extraData = std::make_unique<int[]>(5);
            std::cout << "Child constructor" << std::endl;
        } catch(...) {
            // 如果构造extraData失败,这里可以添加额外的清理逻辑
            std::cerr << "Exception caught during Child construction" << std::endl;
            throw;
        }
    }
    ~Child() {
        std::cout << "Child destructor" << std::endl;
        // 父类析构函数会自动被调用
    }
private:
    std::unique_ptr<int[]> extraData;
};

int main() {
    try {
        Child child;
    } catch(...) {
        std::cerr << "Exception propagated to main" << std::endl;
    }
    return 0;
}

在上述代码中:

  • Parent类在构造函数中分配了动态内存,并在虚析构函数中释放。
  • Child类继承自Parent,在构造函数中使用std::make_unique分配内存,利用RAII机制在析构时自动释放。如果在Child构造函数中分配extraData时抛出异常,由于Parent构造函数已成功执行,Parent的析构函数会被自动调用以释放data。同时,Child类析构函数无需显式释放extraData,因为std::unique_ptr的析构函数会自动完成此操作。在main函数中,使用try - catch块捕获可能从Child构造函数抛出的异常。