RAII
# 介绍
RAII (Resource Acqusition Is Initialization): 是 C++ 中非常 powerful 的一个概念。但是它的名字起的确实很烂,以至于作者都对他起的名字一直感到后悔。更好的名字应该叫做:
- Constructor Acquires, Destructor Releases
- Scope Based Resource Management
# 概念解释
在解释 RAII 前,我们需要先了解什么是 recourse?
Things that we have a finite supply of and so we need to be able to control their usage.
比如: file handler, Heap Memory, Newwork Sockets, Locks (in OS) 都是 resouces.
因为资源是有限的,我们应该在使用时申请资源,用完后需要释放资源。如果不释放,就会造成资源泄漏 (leak),进而可能导致别的程序无法申请到该资源。
以 C 语言中打开文件并打引其中的内容为例子说明:
void printFile(const char* name) {
// acquire file resouce
File* f = fopen(name, "r");
//print contents of f
...
//release file resouce
fclose(f)
}
2
3
4
5
6
7
8
这样的资源管理看起来是没有问题的,需要使用时申请资源,使用完毕后释放资源,但是假设在释放之前出现 return 语句使得函数提前返回或者忘记释放资源,那么被获取的资源就永远得不到释放,产生资源泄漏 (leak)。
# RAII
为了解决这个问题,可以在 C++ 中使用 RAII 对资源进行管理:
我们可以构造一个类,在该类的 Constructor 中申请资源 (Resoruce Acquired when you initialize an object),在 destructor 中释放资源。
这样如果声明了一个这个类的对象,那么就可以在构造对象的时候就获取当前对象所需要的全部资源,使用完毕后自动通过 destructor 释放资源。
因此我们可以创建这样一个类 FileObj ,用来管理资源:
class FileObj {
public:
FILE* ptr;
FileObj(char* name)
: ptr(fopen(name, "r")) {}
~FileObj() {
fclose(ptr);
}
};
2
3
4
5
6
7
8
9
这样我们的 printFile 函数就可以改写为:
void printFile(const char* name) {
// initialization will acquire resources
FileObj fobj(name);
// print contents of f
// FileObj destructor will release resources
}
2
3
4
5
6
7
8
当函数调用结束后,不需要手动释放资源,资源会被自动调用的 destructor 被释放。
参考: