今天看陈硕的多线程书上提到了C++中RAII技术的使用,通过用C11里面自带的智能指针来完成对资源的控制,但是一直不太清楚具体RAII是怎么样的,带着这样的疑问,特地去看了几篇博客,找了一个简单的文件句柄打开关闭RAII管理的实例,瞬间就明白了,这里分享出来。主要从两个部分,首先是RAII技术的介绍,然后是RAII技术的简单实例。
RAII技术的介绍
1、RAII定义
RAII,它是“Resource Acquisition Is Initialization”的首字母缩写。也称为“资源获取就是初始化”,是c++等编程语言常用的管理资源、避免内存泄露的方法。它保证在任何情况下,使用对象时先构造对象,最后析构对象。
RAII的好处在于它提供了一种资源自动管理的方式,当产生异常、回滚等现象时,RAII可以正确地释放掉资源。
当讲述C++资源管理时,Bjarne这样写道:
使用局部对象管理资源的技术通常称为“资源获取就是初始化”。这种通用技术依赖于构造函数和析构函数的性质以及它们与异常处理的交互作用。
2、RAII的理解
看到上面RAII的介绍,发现这不就是栈的资源回收过程中吗??
没错,思想的确是一样的。
我们知道在函数内部的一些成员是放置在栈空间上的,当函数返回时,这些栈上的局部变量就会立即释放空间,于是Bjarne Stroustrup就想到确保能运行资源释放代码的地方就是在这个程序段(栈)中放置的对象的析构函数了,因为stack winding会保证它们的析构函数都会被执行。RAII就利用了栈里面的变量的这一特点。
操作系统中栈的操作过程
Stack Winding & Unwinding
当程序运行时,每一个函数(包括数据、寄存器、程序计数器,等等)在调用时,都被映射到栈上。这就是 stack winding。
Unwinding 是以相反顺序把函数从栈上移除的过程。
正常的 stack unwinding 发生在函数返回时;不正常的情况,比如引发异常,调用setjmp和longjmp,也会导致 stack unwinding。
可见 stack unwinding 的过程中,局部对象的析构函数将逐一被调用。这也就是 RAII 工作的原理,它是由语言和编译器来保证的。
3、RAII做法
RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个存放在栈空间上的局部对象。
这种做法有两大好处:
(1)不需要显式地释放资源。
(2)采用这种方式,对象所需的资源在其生命期内始终保持有效。
那么给出了RAII的做法,问题就是什么是资源,我们来明确资源的概念,在计算机系统中,资源是数量有限且对系统正常运转具有一定作用的元素。
比如,内存,文件句柄,网络套接字(network sockets),互斥锁(mutex locks)等等,它们都属于系统资源。
由于资源的数量不是无限的,有的资源甚至在整个系统中仅有一份,因此我们在使用资源时必须严格遵循的步骤是:
获取资源
使用资源
释放资源
总结
使用RAII,需要自己定义资源类,将自己业务的操作资源封装起来,然后通过这个资源类来完成资源的构造、使用、释放。这样让我们可以放心的去编写逻辑功能的代码,而不用去关心会不会造成内存泄漏这样的问题。
RAII技术的简单实例
正如上面介绍的RAII技术,这里我们给出一个简单的文件句柄打开关闭RAII管理的实例。
一个简单的句柄文件打开关闭的程序:
void Func()
{
FILE *fp;
char* filename = "test.txt";
if((fp=fopen(filename,</