RAII(资源获取即初始化)

RAII(Resource Acquisition Is Initialization)

RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄露问题。

主要作用:

在不失代码简洁性[3]的同时,可以很好地保证代码的异常安全性。

//下面的C++实例说明了如何用RAII访问文件和互斥量:

#include <string>
#include <mutex>
#include <iostream>
#include <fstream>
#include <stdexcept>

void write_to_file(const std::string & message)
{
    // 创建关于文件的互斥锁
    static std::mutex mutex;

    // 在访问文件前进行加锁
    std::lock_guard<std::mutex> lock(mutex);

    // 尝试打开文件
    std::ofstream file("example.txt");
    if (!file.is_open())
        throw std::runtime_error("unable to open file");

    // 输出文件内容
    file << message << std::endl;

    // 当离开作用域时,文件句柄会被首先析构 (不管是否抛出了异常)
    // 互斥锁也会被析构 (同样地,不管是否抛出了异常)
}

C++保证了所有栈对象在生命周期结束时会被销毁(即调用析构函数),所以该代码是异常安全的。无论在write_to_file函数正常返回时,还是在途中抛出异常时,都会引发write_to_file函数的堆栈回退,而此时会自动调用lock和file对象的析构函数。

当一个函数需要通过多个局部变量来管理资源时,RAII就显得非常好用。因为只有被构造成功(构造函数没有抛出异常)的对象才会在返回时调用析构函数,同时析构函数的调用顺序恰好是它们构造顺序的反序,这样既可以保证多个资源(对象)的正确释放,又能满足多个资源之间的依赖关系。

由于RAII可以极大地简化资源管理,并有效地保证程序的正确和代码的简洁,所以通常会强烈建议在C++中使用它。

典型用法

RAII在C++中的应用非常广泛,如C++标准库中的lock_guard便是用RAII方式来控制互斥量:

template <class Mutex> class lock_guard {
private:
    Mutex& mutex_;

public:
    lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); }
    ~lock_guard() { mutex_.unlock(); }

    lock_guard(lock_guard const&) = delete;
    lock_guard& operator=(lock_guard const&) = delete;
};

程序员可以非常方便地使用lock_guard,而不用担心异常安全问题

extern void unsafe_code();  // 可能抛出异常

using std::mutex;
using std::lock_guard;

mutex g_mutex;

void access_critical_section()
{
    lock_guard<mutex> lock(g_mutex);
    unsafe_code();
}

猜你喜欢

转载自blog.csdn.net/weixin_39554266/article/details/80353929