C++号称是多范式的通用编程语言,但是RAII实际上已在C++编程技术中变成 不可或缺的核心技术。RAII几乎无处不在的身影不仅仅来自于C++之父的大力提 倡,更来自于这一技术本身的简单,高效和几乎无所不能的适应面。
如 果您还没有听说过RAII的话,那么我在这里再重新叙述一遍,RAII是下列英文短 语的首字母缩写:
Resource Acquisition Is Initialisation
这 句话直译为中文的意思是: 资源获得即初始化。这只是一个短语,不能指望靠 望文生义来了解字面背后的完整含义,但是短语本身的确反映了重要的论点: 资源是其一,初始化是其二。
RAII 是有关资源的。资源是一切需要分配 的数量有限的资料。比如,存储器,文件句柄,网络套接字端口,数据库连接, 以及线程池等。基本上,由于物理的限制,所有的资料都是有限的。在某些特殊 的情况下,资料由于局部的极大丰富而丧失了资源的意义,比如沙子,空气等。 但是在大多数情况下,资料都是有限的,需要我们善加管理。
资源管理 的最基本形式就是善始善终。申请了资源,用完了,就要归还。在C++程序员生 活里最常见的就是内存资源,资源管理就是内存管理: 申请了内存,不管什么 时候逻辑上完成了对这片内存的使用,内存就要被正确地释放。注意这里的用词 是"不管什么时候". 在实际应用中,内存的使用逻辑是如此复杂,使 得逻辑上界定某块内存的生命周期会成为非常繁琐非常复杂的任务,而内存资源 就会在人类智力的疏漏中泄漏出去。而即使是简单情况,内存也会在菜鸟程序员 手忙脚乱的拙劣中溜走。所以资源管理虽然可以简化为一句"有始有终 ",在实际当中很难得到保证。
有 一类语言,比如Java,把内存资 源接管了,提供了所谓的自动内存管理,使用内存分配算法的方式为程序员模拟 了一个取之不尽用之不竭的准无穷内存模式。背后的思想是,在普通应用中,内 存的使用在空间和时间上都是相对集中的,这就允许用较少的内存来应付时间积 累上无限的内存请求。程序员使用这类语言就不用再考虑内存的释放问题。负担 就大大减轻了。
自动内存管理从原理上把内存资源倍增而产生一种资料 (准)无限的虚拟环境,从而把程序员从繁重的内存资源管理上解放出来,化更 多的精力考虑实际的事务代码,提高了生产率。但是它也有自己的局限。一,自 动内存管理算法比较复杂,本身的程序就要占一定内存,同时自动内存管理用时 间换空间,还要求实际物理内存至少为应用最大瞬时所需内存的两倍才能较好地 发挥作用,这一要求说明,自动内存管理其实已经不是在管理短缺意义上的 "资源",而是为不那么浪费地使用丰富的资料提供一种说得过去的代 用方案。其次,由于自动内存管理是与具体的应用分离的,无法知道最合适的切 入点,所以自动内存管理的介入基本是不可预测的。这限制了自动内存管理在那 些对时间响应要求比较严格的程序中的应用。最后,自动内存管理仅是对内存资 源的管理,它无法管理其它的资源。除了内存,程序员往往要和其它的资源打交 道。自动内存管理模式无法应用到其它类型的资源管理。
C++提供了RAII 作为一个真正意义上的资源管理实用方案。这也是C++语言在资源管理这一意义 更加广泛的问题上作出的贡献。虽然其实用意义如此重大,但是其做法却很简单 ,就是用类来表示资源,在类的构造函数里分配资源,在类的析构函数里释放资 源。比如,
class Resource {
public:
Resource(const char *name) : _resource(alloc_resource(name)) {……}
~Resource() { release_resource (_resource); }
};
资源类的使用也很简单,按 泛围使用。比如,有一个事务处理,使用到了某种资源。如果这一事务可以用一 个函数来表示,那么,可以简单地用一个在函数入口处分配的资源变量来表示资 源分配。例如:
void transaction1(const char *res_name)
{
Resource res(res_name);
// 后面 是使用资源res
}