**RAII**，全称**资源获取即初始化**（英語：），它是在一些面向对象语言中的一种慣用法（英语：）。RAII源于C++，在Java，C#，D，Ada，Vala和Rust中也有应用。1984-1989年期间，比雅尼·斯特勞斯特魯普和安德鲁·柯尼希（英语：）在设计C++异常时，为解决資源管理（英语：）时的異常安全（英语：）性而使用了该用法，后来比雅尼·斯特勞斯特魯普将其称为RAII。

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

作用
--

RAII的主要作用是在不失代码简洁性的同时，可以很好地保证代码的异常安全性。

下面的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();
}

```

实际上，C++标准库的实现就广泛应用了RAII，典型的如容器、智能指针等。

RRID
----

RAII还有另外一种被称为RRID(Resource Release Is Destruction)的特殊用法，即在构造时没有"获取"资源，但在析构时释放资源。ScopeGuard和Boost.ScopeExit就是RRID的典型应用：

```
#include <functional>

class ScopeGuard {
private:
 typedef std::function<void()> destructor\_type;

 destructor\_type destructor\_;
 bool dismissed\_;

public:
 ScopeGuard(destructor\_type destructor) : destructor\_(destructor), dismissed\_(false) {}

 ~ScopeGuard()
 {
 if (!dismissed\_) {
 destructor\_();
 }
 }

 void dismiss() { dismissed\_ = true; }

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

```

ScopeGuard通常用于省去一些不必要的RAII封装，例如

```
void foo()
{
 auto fp = fopen("/path/to/file", "w");
 ScopeGuard fp\_guard([&fp]() { fclose(fp); });

 write\_to\_file(fp); // 异常安全
}

```

在D语言中，scope关键字也是典型的RRID用法，例如

```
void access\_critical\_section()
{
 Mutex m = new Mutex;
 lock(m); 
 scope(exit) unlock(m);

 unsafe\_code(); // 异常安全
}

Resource create()
{
 Resource r = new Resource();
 scope(failure) close(f);

 preprocess(r); // 抛出异常时会自动调用close(r)
 return r;
}

```

與finally的比較
-----------

虽然RAII和finally都能保证资源管理时的异常安全，但相对来说，使用RAII的代码相对更加简洁。
如比雅尼·斯特劳斯特鲁普所说，"在真实环境中，调用资源释放代码的次数远多于资源类型的个数，所以相对于使用finally来说，使用RAII能减少代码量。"

例如在Java中使用finally来管理Socket资源

```
void foo() {
 Socket socket;
 try {
 socket = new Socket();
 access(socket);
 } finally {
 socket.close();
 }
}

```

在采用RAII后，代码可以简化为

```
void foo() {
 try (Socket socket = new Socket()) {
 access(socket);
 }
}

```

特别是当大量使用Socket时，重复的finally就显得没有必要。

参考资料
----

* Stroustrup, Bjarne.  [C++程序设计语言]. Addison-Wesley. 2000. ISBN 0-201-70073-5 （英语）.
* Stroustrup, Bjarne.  [C++语言的设计和演化]. Addison-Wesley. 1994. ISBN 0-201-54330-3 （英语）.
* Wilson, Matthew.  [Imperfect C++中文版]. Addison-Wesley. 2004. ISBN 0321228774 （英语）.

1. ↑  Exception Handling for C++ （页面存档备份，存于）, 5 Handling of Destructors
2. ↑  Stroustrup 1994，chpt. 16.5 Resource Management. I called this technique "resource acquisition is initialization."
3. ↑  C++ FAQ, "I have too many try blocks; what can I do about it?" （页面存档备份，存于）
4. 1 2  Stroustrup 2000，chpt. 14.4.1 Using Constructors and Destructors.
5. ↑  C++ FAQ, "What's the order that local objects are destructed?" （页面存档备份，存于）
6. ↑  lock\_guard（页面存档备份，存于）
7. ↑  Wilson 2004，chpt. 3.4 RRID.
8. ↑  Andrei Alexandrescu, Change the Way You Write Exception-Safe Code （页面存档备份，存于）
9. ↑  Boost.ScopeExit（页面存档备份，存于）
10. ↑  Bjarne Stroustrup's C++ Style and Technique FAQ （页面存档备份，存于）. "Why doesn't C++ provide a "finally" construct?" （页面存档备份，存于）