MST

星途 面试题库

面试题:C++类析构函数在复杂继承体系下的资源管理

有一个C++的继承体系,基类在构造函数中分配了一些系统级资源(如共享内存段的映射),并在析构函数中释放。派生类除了继承基类的资源外,还在构造函数中初始化了一些动态加载的插件模块资源,在析构函数中需要卸载这些插件模块。请描述如何设计整个继承体系的构造和析构函数,以确保在对象创建和销毁过程中资源管理的正确性和高效性,尤其要注意在多重继承和虚继承场景下可能出现的问题。
39.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 单一继承场景
    • 基类
      • 构造函数:负责分配系统级资源,如共享内存段的映射。例如:
class Base {
public:
    Base() {
        // 分配共享内存段映射的代码
        // 假设这里有一个函数 mapSharedMemory 来进行映射
        sharedMemory = mapSharedMemory(); 
    }
    ~Base() {
        // 释放共享内存段映射的代码
        // 假设这里有一个函数 unmapSharedMemory 来进行解映射
        unmapSharedMemory(sharedMemory); 
    }
private:
    void* sharedMemory;
};
 - **派生类**:
   - **构造函数**:首先调用基类构造函数,然后初始化动态加载的插件模块资源。例如:
class Derived : public Base {
public:
    Derived() {
        // 调用基类构造函数后,初始化插件模块
        plugin = loadPlugin(); 
    }
    ~Derived() {
        // 卸载插件模块
        unloadPlugin(plugin); 
    }
private:
    void* plugin;
};
  1. 多重继承场景
    • 基类1(负责系统级资源):构造函数和析构函数与单一继承中的基类类似。
class Base1 {
public:
    Base1() {
        sharedMemory = mapSharedMemory(); 
    }
    ~Base1() {
        unmapSharedMemory(sharedMemory); 
    }
private:
    void* sharedMemory;
};
  • 基类2(负责其他资源,假设为数据库连接):构造函数分配资源,析构函数释放资源。
class Base2 {
public:
    Base2() {
        dbConnection = establishDBConnection(); 
    }
    ~Base2() {
        closeDBConnection(dbConnection); 
    }
private:
    void* dbConnection;
};
  • 派生类:构造函数按照声明顺序调用基类构造函数,然后初始化自己的插件模块资源。析构函数按构造函数相反顺序释放资源。
class Derived : public Base1, public Base2 {
public:
    Derived() {
        plugin = loadPlugin(); 
    }
    ~Derived() {
        unloadPlugin(plugin); 
    }
private:
    void* plugin;
};
  1. 虚继承场景
    • 虚基类(负责公共资源,如日志文件句柄)
      • 构造函数:分配资源。
class VirtualBase {
public:
    VirtualBase() {
        logFile = openLogFile(); 
    }
    ~VirtualBase() {
        closeLogFile(logFile); 
    }
private:
    FILE* logFile;
};
  • 中间类1(继承虚基类并增加自己的资源,如网络套接字)
    • 构造函数:调用虚基类构造函数,然后分配自己的资源。
class Intermediate1 : virtual public VirtualBase {
public:
    Intermediate1() {
        socket = createSocket(); 
    }
    ~Intermediate1() {
        closeSocket(socket); 
    }
private:
    int socket;
};
  • 中间类2(继承虚基类并增加自己的资源,如文件描述符)
    • 构造函数:调用虚基类构造函数,然后分配自己的资源。
class Intermediate2 : virtual public VirtualBase {
public:
    Intermediate2() {
        fileDescriptor = openFile(); 
    }
    ~Intermediate2() {
        closeFile(fileDescriptor); 
    }
private:
    int fileDescriptor;
};
  • 最终派生类
    • 构造函数:虽然虚基类构造函数会被中间类调用,但为了明确和一致性,在最终派生类构造函数初始化列表中也列出虚基类。然后调用中间类构造函数,最后初始化自己的插件模块资源。
class FinalDerived : public Intermediate1, public Intermediate2 {
public:
    FinalDerived() : VirtualBase(), Intermediate1(), Intermediate2() {
        plugin = loadPlugin(); 
    }
    ~FinalDerived() {
        unloadPlugin(plugin); 
    }
private:
    void* plugin;
};

注意事项

  • 在多重继承和虚继承中,要明确构造函数和析构函数的调用顺序,避免资源泄漏和未定义行为。
  • 虚继承时,虚基类的构造函数由最底层的派生类调用,以确保资源的正确初始化和唯一性。
  • 所有资源分配和释放操作应确保异常安全性,例如在构造函数中,如果资源分配失败,应确保已分配的其他资源被正确释放。