RAII是指C++语言中的一个惯用法(idiom),它是“ResourceAcquisition Is Initialization”的首字母缩写。中文可将其翻译为“资源获取就是初始化”。虽然从某种程度上说这个名称并没有体现出该惯性法的本质精神,但是作为标准C++资源管理的关键技术,RAII早已在C++社群中深入人心。
我记得第一次学到RAII惯用法是在Bjarne Stroustrup的《C++程序设计语言(第3版)》一书中。当讲述C++资源管理时,Bjarne这样写道:
使用局部对象管理资源的技术通常称为“资源获取就是初始化”。这种通用技术依赖于构造函数和析构函数的性质以及它们与异常处理的交互作用。
Bjarne这段话是什么意思呢?
首先让我们来明确资源的概念,在计算机系统中,资源是数量有限且对系统正常运转具有一定作用的元素。比如,内存,文件句柄,网络套接字(network sockets),互斥锁(mutex locks)等等,它们都属于系统资源。由于资源的数量不是无限的,有的资源甚至在整个系统中仅有一份,因此我们在使用资源时必须严格遵循的步骤是:
1. 获取资源
2. 使用资源
3. 释放资源
例如在下面的UseFile函数中:
void UseFile(char const* fn)
{
FILE* f = fopen(fn, "r"); //获取资源
// 在此处使用文件句柄f... // 使用资源
fclose(f); // 释放资源
}
调用fopen()打开文件就是获取文件句柄资源,操作完成之后,调用fclose()关闭文件就是释放该资源。资源的释放工作至关重要,如果只获取而不释放,那么资源最终会被耗尽。上面的代码是否能够保证在任何情况下都调用fclose函数呢?请考虑如下情况:
void UseFile(char const* fn)
{
FILE* f = fopen(fn, "r"); // 获取资源
// 使用资源
if (!g()) return; // 如果操作g失败!
// ...
if (!h()) return; // 如果操作h失败!
// ...
fclose(f); // 释放资源
}
在使用文件f的过程中,因某些操作失败而造成函数提前返回的现象经常出现。这时函数UseFile的执行流程将变为: