RAII是Resource Acquisition Is Initialization的缩写,即 “资源获取即初始化”。
RAII 是C++语言的一种管理资源、避免泄漏的惯用法。简单点来说,就是在构造对象的时候获取资源,在使用的时候调用资源,最后析构的时候销毁资源来达到资源的管理和避免泄露。
谈到资源,大家第一时间想到的应该是内存,即还有多少内存空间可用,实际上从广义角度讲,资源还可以指很多东西,比如网络连接(connect),句柄(socket),事件(event),线程(thread)等等。这些都可以当作资源,之前跟一个大佬学习也是这样做的,于是我才发现这是一种惯用的思维,大家按照这种写法可以做到使资源安全的释放掉。
在介绍中我们讲了RAII惯用法的三大步骤,获取资源 , 使用资源 , 销毁资源,那么我们的代码也要紧跟着这三步走。
先获取一个资源,然后对资源进行操作,操作完了以后再销毁
void solve() {
File* file = fopen(fn , "r")
do1(file);//操作一
do2(file);//操作二
fclose(file);
}
但是我们会遇到一些问题在操作中我们可能遇到异常,需要结束solve() , 这需要在每一次异常时将在局部中获取的资源释放,非常的容易遗忘和维护,这样做会让代码看起来很臃肿,并且使代码效率下降很多,维护的时间也会大大增加。
列如
void solve() {
File* file = fopen(fn , "r")
try{
if(!do1(file))
fclose(file);//操作一
if(!do2(file))
fclose(file);
}
catch(....)
{
fclose(f); // 释放资源
throw;
}
fclose(file);
}
RAII惯用法可以很大程度的弥补这些写法的不足
在里面我们将File抽象成一个类FileEvent,将一个局部对象来表示这个File资源,在系统判断局部对象生命周期结束的时候就会自动调用FileEvent的析构函数来把这个资源自动删除。
如若使用文件file的代码中有异常抛出,难道析构函数还会被调用吗?此时RAII还能如此奏效吗?
问得好。事实上,当一个异常抛出之后,系统沿着函数调用栈,向上寻找catch子句的过程,称为栈辗转开解(stack unwinding)。
class FileEvent {
public:
FileEvent(char const* fn, char const* ch) { p = fopen(fn, ch);//获取资源 }
~FileEvent() { fclose(p); //释放资源}
private:
FILE *p;
};
void Solve()
{
FileEvent file1("n1.txt", "r");
Do(); //若抛出异常,一样会调用析构
FileHandle file2("n2.txt", "rw")
}
当到Do()时,file1对象已经创建成功了,file2还未创建,如果Do() 中抛出了异常的话,file1的构造函数就会调用(析构函数的调用顺序和构造函数的调用顺序相反 , 假设先构造顺序为f1,f2,那么析构顺序则为f2,f1)。
RAII实际上是一种设计模式,该设计模式的使用位置应该是对于资源对象的管理,这样写的出的代码不仅优雅并且简洁,而且还做到了异常安全。
非常的好用~。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。