当前位置: 首页 > article >正文

RAII(Resource Acquisition Is Initialization)机制

RAII(Resource Acquisition Is Initialization)机制

1. 什么是 RAII?

🌟 RAII(资源获取即初始化,Resource Acquisition Is Initialization)C++ 语言中的一种管理资源的编程技巧
RAII 使资源(如内存、文件句柄、线程、锁等)与对象的生命周期绑定
这意味着:

  • 对象构造时(constructor)申请资源
  • 对象析构时(destructor)自动释放资源

2. RAII 解决了什么问题?

RAII 解决了 手动管理资源 的各种问题,例如:

  • 忘记释放资源(导致内存泄漏文件句柄泄漏
  • 异常导致资源释放失败(如果函数 throw 异常,资源可能不会被释放)
  • 资源管理逻辑分散,代码难以维护

RAII 确保资源不会泄漏,并且异常不会破坏资源管理,从而 提高代码的安全性和可维护性


3. RAII 代码示例

RAII 典型的做法是:使用类的构造函数获取资源,使用析构函数释放资源

🔥 示例 1:FILE 文件句柄管理(避免 fopen() 忘记 fclose()

#include <iostream>
#include <cstdio>

class FileHandler {
public:
    explicit FileHandler(const char* filename) {
        file_ = std::fopen(filename, "w");
        if (!file_) {
            throw std::runtime_error("无法打开文件!");
        }
    }

    ~FileHandler() {  // RAII: 在析构函数中释放资源
        if (file_) {
            std::fclose(file_); 
            std::cout << "文件已关闭\n";
        }
    }

    void write(const char* text) {
        if (file_) {
            std::fprintf(file_, "%s", text);
        }
    }

private:
    FILE* file_; // 资源:文件指针
};

int main() {
    try {
        FileHandler fh("example.txt");  // 构造时打开文件
        fh.write("Hello, RAII!\n");

    } catch (const std::exception& e) {
        std::cerr << "异常:" << e.what() << std::endl;
    } 
    // FileHandler 对象 `fh` 作用域结束后,自动释放文件资源
}

📌 这里的 RAII 机制:

  • 构造函数 (FileHandler::FileHandler()) 申请 fopen() 资源
  • 析构函数 (FileHandler::~FileHandler()) 释放 fclose() 资源
  • 即使异常发生,析构函数依旧会执行,确保不会忘记释放资源

🔥 示例 2:智能指针(避免 new 后忘记 delete
在 C++98 及之前,动态分配对象需要手动使用 new/delete

void bad_example() {
    int* p = new int(10); // 申请内存
    throw std::runtime_error("发生异常!"); // Oops! `delete p;` 没有执行,内存泄漏!
    delete p; // 永远不会执行
}

RAII 解决这个问题,C++11 以后我们可以使用 std::unique_ptr

#include <memory>
void good_example() {
    std::unique_ptr<int> p = std::make_unique<int>(10); // RAII:构造时申请资源
    throw std::runtime_error("发生异常!"); // 资源仍然会被自动释放,不会泄漏!
} // 作用域结束,自动调用 `unique_ptr` 析构函数,释放 `int*`

📌 这里的 RAII 机制:

  • std::unique_ptr<int> 绑定了 new int(10) 的生命周期
  • 异常发生时,unique_ptr 自动释放 int*,不会导致内存泄漏

🔥 示例 3:C++ std::lock_guard 保护互斥锁
手动加锁和解锁,非常容易遗漏:

std::mutex mtx;
void bad_function() {
    mtx.lock();  // 手动加锁
    throw std::runtime_error("发生异常!");  // Oops! 没有 `unlock()`,死锁风险!
    mtx.unlock();
}

RAII 方式(使用 std::lock_guard):

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void safe_function() {
    std::lock_guard<std::mutex> guard(mtx); // RAII:构造时加锁,析构时自动解锁
    std::cout << "安全访问共享资源\n";
    // 作用域结束 `lock_guard` 析构时,自动解锁,避免死锁风险
}

4. LoanedCommandInterface 是如何使用 RAII 的?

ROS 2 hardware_interface::LoanedCommandInterface 也是 RAII 设计模式 的一个典型应用:

class LoanedCommandInterface {
public:
    explicit LoanedCommandInterface(CommandInterface* cmd)
        : command_interface_(cmd) {}

    ~LoanedCommandInterface() { command_interface_ = nullptr; } // RAII 释放资源

    double get_value() { return command_interface_->get_value(); }
    void set_value(double value) { command_interface_->set_value(value); }

private:
    CommandInterface* command_interface_;
};

📌 RAII 这里的作用:

  • 构造函数 LoanedCommandInterface() 申请硬件接口
  • 析构函数 ~LoanedCommandInterface() 自动释放硬件接口
  • 即使异常发生,析构仍然会执行,防止资源泄漏

5. 什么时候使用 RAII?

✅ 当你需要手动管理资源(如 内存、文件、锁、网络连接、数据库连接)时,尽量使用 RAII:

  • 替代 new/delete —— 使用 std::unique_ptr
  • 替代 malloc/free —— 使用 std::vector(内部自动管理 new[]/delete[]
  • 处理文件 —— 用 std::fstream,避免 fopen/fclose
  • 管理线程同步 —— 用 std::lock_guard 自动管理 std::mutex

6. RAII 适用于 C++,但在 C 语言中常见吗?

C 语言本身 不支持 RAII,因为 C 没有构造函数/析构函数,必须手动释放资源:

FILE* file = fopen("data.txt", "w");
if (!file) { return -1; }
// 使用文件...
fclose(file);  // 记得手动释放资源

在 C++ 里,我们可以使用 RAII 方式,减少手动释放资源的风险

std::ofstream file("data.txt");

文件会在 std::ofstream 对象销毁时 自动关闭,避免了 C 语言里可能出现的 资源泄漏


7. 总结

🔹 RAII(资源获取即初始化)是 C++ 的重要设计模式
🔹 在构造函数获取资源,在析构函数释放资源,防止泄漏
🔹 RAII 适用于内存(智能指针)、文件、锁、线程、数据库连接等
🔹 std::unique_ptrstd::vectorstd::lock_guard 都是 RAII 典型应用
🔹 hardware_interface::LoanedCommandInterface 也是基于 RAII 的设计

🚀 RAII 增强代码的安全性、可维护性,尽量在 C++ 编程中使用 RAII! 🚀


http://www.kler.cn/a/548085.html

相关文章:

  • Ubuntu 24.04 上安装 Nginx
  • C++经典习题
  • 【Python爬虫(1)】专栏开篇:夯实Python基础
  • 服务器被暴力破解的一次小记录
  • 【Docker】Docker中卷的类型、区别及应用
  • 8、k8s的pv和pvc
  • 小白零基础如何用cursor
  • electron打包基本教程
  • 电解电容的参数指标
  • DevOps自动化部署详解:从理念到实践
  • Android车机DIY开发之软件篇(十六)编译forlinx i.mx8mplus Android
  • Next.js国际化:next-i18next
  • 【C】初阶数据结构4 -- 双向循环链表
  • Python PyCharm DeepSeek接入
  • LeetCode1706
  • Windows环境下使用Ollama搭建本地AI大模型教程
  • 平面与平面相交算法杂谈
  • flink实时集成利器 - apache seatunnel - 核心架构详解
  • 文心一言还有哪些待优化的地方
  • [leetcode] 动态规划 - 最大子数组和