
_START_ARTICLE_
RAII
_START_PARAGRAPH_
RAII全称为Resource Acquisition Is Initialization，它是在一些面向对象语言中的一种惯用法。RAII源于C++，在Java，C#，D，Ada，Vala和Rust中也有应用。1984-1989年期间，比雅尼·斯特劳斯特鲁普和安德鲁·柯尼希在设计C++异常时，为解决资源管理时的异常安全性而使用了该用法，后来比雅尼·斯特劳斯特鲁普将其称为RAII。_NEWLINE_RAII要求，资源的有效期与持有资源的对象的生命期严格绑定，即由对象的构造函数完成资源的分配（获取），同时由析构函数完成资源的释放。在这种要求下，只要对象能正确地析构，就不会出现资源泄露问题。
_START_SECTION_
作用
_START_PARAGRAPH_
RAII的主要作用是在不失代码简洁性的同时，可以很好地保证代码的异常安全性。_NEWLINE_下面的C++实例说明了如何用RAII访问文件和互斥量：_NEWLINE_#include <string>#include <mutex>#include <iostream>#include <fstream>#include <stdexcept>_NEWLINE_ _NEWLINE_void write_to_file(const std::string & message){_NEWLINE_    // 创建关于文件的互斥锁_NEWLINE_    static std::mutex mutex;_NEWLINE_ _NEWLINE_    // 在访问文件前进行加锁_NEWLINE_    std::lock_guard<std::mutex> lock(mutex);_NEWLINE_ _NEWLINE_    // 尝试打开文件_NEWLINE_    std::ofstream file("example.txt");_NEWLINE_    if (!file.is_open())_NEWLINE_        throw std::runtime_error("unable to open file");_NEWLINE_ _NEWLINE_    // 输出文件内容_NEWLINE_    file << message << std::endl;_NEWLINE_ _NEWLINE_    // 当离开作用域时，文件句柄会被首先析构(不管是否抛出了异常)_NEWLINE_    // 互斥锁也会被析构(同样地，不管是否抛出了异常)}_NEWLINE_C++保证了所有栈对象在生命周期结束时会被销毁(即调用析构函数)，所以该代码是异常安全的。无论在write_to_file函数正常返回时，还是在途中抛出异常时，都会引发write_to_file函数的堆栈回退，而此时会自动调用lock和file对象的析构函数。_NEWLINE_当一个函数需要通过多个局部变量来管理资源时，RAII就显得非常好用。因为只有被构造成功(构造函数没有抛出异常)的对象才会在返回时调用析构函数，同时析构函数的调用顺序恰好是它们构造顺序的反序，这样既可以保证多个资源(对象)的正确释放，又能满足多个资源之间的依赖关系。_NEWLINE_由于RAII可以极大地简化资源管理，并有效地保证程序的正确和代码的简洁，所以通常会强烈建议在C++中使用它。
_START_SECTION_
典型用法
_START_PARAGRAPH_
RAII在C++中的应用非常广泛，如C++标准库中的lock_guard便是用RAII方式来控制互斥量:_NEWLINE_template <class Mutex> class lock_guard {private:_NEWLINE_    Mutex& mutex_;public:_NEWLINE_    lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); }_NEWLINE_    ~lock_guard() { mutex_.unlock(); }_NEWLINE__NEWLINE_    lock_guard(lock_guard const&) = delete;_NEWLINE_    lock_guard& operator=(lock_guard const&) = delete;};_NEWLINE_程序员可以非常方便地使用lock_guard，而不用担心异常安全问题_NEWLINE_extern void unsafe_code();  // 可能抛出异常using std::mutex;using std::lock_guard;mutex g_mutex;void access_critical_section(){_NEWLINE_    lock_guard<mutex> lock(g_mutex);_NEWLINE_    unsafe_code();}_NEWLINE_实际上，C++标准库的实现就广泛应用了RAII，典型的如容器、智能指针等。
_START_SECTION_
RRID
_START_PARAGRAPH_
RAII还有另外一种被称为RRID(Resource Release Is Destruction)的特殊用法，即在构造时没有“获取”资源，但在析构时释放资源。ScopeGuard和Boost.ScopeExit就是RRID的典型应用：_NEWLINE_#include <functional>class ScopeGuard {private:_NEWLINE_    typedef std::function<void()> destructor_type;_NEWLINE__NEWLINE_    destructor_type destructor_;_NEWLINE_    bool dismissed_;public:_NEWLINE_    ScopeGuard(destructor_type destructor) : destructor_(destructor), dismissed_(false) {}_NEWLINE__NEWLINE_    ~ScopeGuard()_NEWLINE_    {_NEWLINE_        if (!dismissed_) {_NEWLINE_            destructor_();_NEWLINE_        }_NEWLINE_    }_NEWLINE__NEWLINE_    void dismiss() { dismissed_ = true; }_NEWLINE__NEWLINE_    ScopeGuard(ScopeGuard const&) = delete;_NEWLINE_    ScopeGuard& operator=(ScopeGuard const&) = delete;};_NEWLINE_ScopeGuard通常用于省去一些不必要的RAII封装，例如_NEWLINE_void foo(){_NEWLINE_    auto fp = fopen("/path/to/file", "w");_NEWLINE_    ScopeGuard fp_guard([&fp]() { fclose(fp); });_NEWLINE__NEWLINE_    write_to_file(fp);                     // 异常安全}_NEWLINE_在D语言中，scope关键字也是典型的RRID用法，例如_NEWLINE_void access_critical_section(){_NEWLINE_    Mutex m = new Mutex;_NEWLINE_    lock(m); _NEWLINE_    scope(exit) unlock(m);_NEWLINE__NEWLINE_    unsafe_code();                  // 异常安全}Resource create(){_NEWLINE_    Resource r = new Resource();_NEWLINE_    scope(failure) close(f);_NEWLINE__NEWLINE_    preprocess(r);                  // 抛出异常时会自动调用close(r)_NEWLINE_    return r;}
_START_SECTION_
与finally的比较
_START_PARAGRAPH_
虽然RAII和finally都能保证资源管理时的异常安全，但相对来说，使用RAII的代码相对更加简洁。_NEWLINE_如比雅尼·斯特劳斯特鲁普所说，“在真实环境中，调用资源释放代码的次数远多于资源类型的个数，所以相对于使用finally来说，使用RAII能减少代码量。”_NEWLINE_例如在Java中使用finally来管理Socket资源_NEWLINE_void foo() {_NEWLINE_    Socket socket;_NEWLINE_    try {_NEWLINE_        socket = new Socket();_NEWLINE_        access(socket);_NEWLINE_    } finally {_NEWLINE_        socket.close();_NEWLINE_    }}_NEWLINE_在采用RAII后，代码可以简化为_NEWLINE_void foo() {_NEWLINE_    try (Socket socket = new Socket()) {_NEWLINE_        access(socket);_NEWLINE_    }}_NEWLINE_特别是当大量使用Socket时，重复的finally就显得没有必要。