MST

星途 面试题库

面试题:C++ RAII与智能指针在复杂资源管理中的深度剖析

假设存在一个复杂的资源管理场景,需要管理网络连接、文件句柄以及数据库事务,并且这些资源之间存在依赖关系。请结合C++ RAII和智能指针,设计一个完整的资源管理方案,说明如何处理资源初始化、释放以及异常安全等问题,要求给出详细的类设计和代码实现。
35.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 类设计思路

  • 网络连接类 (NetworkConnection):封装网络连接相关操作,利用RAII在析构函数中关闭连接。
  • 文件句柄类 (FileHandle):封装文件操作相关,利用RAII在析构函数中关闭文件。
  • 数据库事务类 (DatabaseTransaction):封装数据库事务操作,利用RAII在析构函数中提交或回滚事务。
  • 资源管理器类 (ResourceManager):管理上述三种资源,利用智能指针确保资源的正确初始化、释放和异常安全。

2. 代码实现

#include <iostream>
#include <memory>
#include <string>

// 网络连接类
class NetworkConnection {
public:
    NetworkConnection(const std::string& ip, int port) {
        std::cout << "Connecting to " << ip << ":" << port << std::endl;
        // 实际连接操作代码
    }
    ~NetworkConnection() {
        std::cout << "Closing network connection" << std::endl;
        // 实际关闭连接操作代码
    }
};

// 文件句柄类
class FileHandle {
public:
    FileHandle(const std::string& filename) {
        std::cout << "Opening file " << filename << std::endl;
        // 实际打开文件操作代码
    }
    ~FileHandle() {
        std::cout << "Closing file" << std::endl;
        // 实际关闭文件操作代码
    }
};

// 数据库事务类
class DatabaseTransaction {
public:
    DatabaseTransaction() {
        std::cout << "Starting database transaction" << std::endl;
        // 实际开始事务操作代码
    }
    ~DatabaseTransaction() {
        std::cout << "Rolling back database transaction" << std::endl;
        // 实际回滚事务操作代码
    }
    void commit() {
        std::cout << "Committing database transaction" << std::endl;
        // 实际提交事务操作代码
    }
};

// 资源管理器类
class ResourceManager {
public:
    ResourceManager(const std::string& ip, int port, const std::string& filename)
        : networkConn(std::make_unique<NetworkConnection>(ip, port)),
          fileHandle(std::make_unique<FileHandle>(filename)),
          dbTransaction(std::make_unique<DatabaseTransaction>()) {
        std::cout << "All resources initialized" << std::endl;
    }

    ~ResourceManager() {
        std::cout << "ResourceManager destructor: Releasing all resources" << std::endl;
        // 智能指针会自动调用各自资源的析构函数
    }

    void doWork() {
        try {
            // 在这里执行依赖这些资源的业务逻辑
            std::cout << "Doing work with network, file and database" << std::endl;
            // 模拟异常情况
            throw std::runtime_error("Simulated error"); 
            dbTransaction->commit();
        } catch (const std::exception& e) {
            std::cerr << "Exception caught: " << e.what() << std::endl;
            // 异常情况下,智能指针会自动释放资源
        }
    }

private:
    std::unique_ptr<NetworkConnection> networkConn;
    std::unique_ptr<FileHandle> fileHandle;
    std::unique_ptr<DatabaseTransaction> dbTransaction;
};

3. 测试代码

int main() {
    try {
        ResourceManager manager("127.0.0.1", 8080, "example.txt");
        manager.doWork();
    } catch (const std::exception& e) {
        std::cerr << "Unhandled exception in main: " << e.what() << std::endl;
    }
    return 0;
}

4. 说明

  • 资源初始化:在ResourceManager的构造函数中,通过std::make_unique创建三种资源的实例,确保资源被正确初始化。
  • 资源释放ResourceManager的析构函数依赖智能指针std::unique_ptr的自动析构特性,当ResourceManager对象销毁时,会自动调用所管理资源的析构函数,释放网络连接、文件句柄和数据库事务。
  • 异常安全:在doWork函数中,若发生异常,由于资源由智能指针管理,它们会在异常离开作用域时自动释放,避免资源泄漏。同时,DatabaseTransaction在异常情况下会回滚事务,保证数据一致性。