C++必知必会 - RAII(资源获取及初始化)

RAII(资源获取及初始化),即resource acquisition is initialization;他不是某些人以为的“初始化即资源获取”(initialization is resource acquisition)。这对于一些重要资源非常重要,比如内存,文件句柄,网络连接,数据库连接以及审计追踪(audit trail)

 

PAII基本技术原理很简单,如果希望保持对某个重要资源的跟踪,那么创建一个对象,并将资源的生命期和对象的生命期相关联。这样的话,就可以利用c++复杂老练的对象管理机制来管理资源。最简单的形式是,当你构造一个对象的时候,其构造对象会获得一份资源,而析构函数则释放这份资源。

class Resoruce{…}   //一个资源类

class ResourceHandle

{

public:

     explicit ResourceHandle(Resource *aResource):r_(aReasource){}   //获取资源

     ~ResourceHandle()

            {delete r_;}  //释放资源

    Resource *get()

{return r_;} //访问资源

private:

   ResourceHandle(const ResourceHandle &);

   ResourceHandle &operator = (const operator &);

   Resource   *r_;

};

void

 example_usage(

)

 {


    File logfile(

"logfile.txt"

)

;

 // open file (acquire resource)


    logfile.write

(

"hello logfile!"

)

;



// continue using logfile ... // throw exceptions or return without // worrying about closing the log; // it is closed automatically when // logfile goes out of scope
//...

if(IFeelLikeIt()) // NO problem here!
return;
//...
AnotherFunCall(); // exception thrown? No!

//destructor is called for logfile here
 }
当ResourceHandle对象被用于一个局部变量,或作为函数的参数,或是一个静态变量,我们都可以保证析构函数会得到调用从而释放对象
所引用的资源。即使碰到意外的return、异常甚至是邪恶的goto,均是如此。
所注意的是如果ResourceHandle被分配到堆上,我们只有显式的delete该ResourceHandle对象,才能确保析构函数被调用。比如,如果
上面的logfile是分配在堆上,考虑一下是会出现如何糟糕的情况。

背后的c++对象管理机制

C++

 allow objects to be allocated on the stack

 and their scoping

 rules ensure that destructors are called when a local 
object's scope ends. By putting the resource release logic in the destructor, C++'s scoping provide direct support
 for RAII.
In this language, the only code that can be guaranteed to be executed after an exception is thrown are the destructors of
 objects residing on the stack . Resources therefore need to be tied to the lifespan of suitable objects in order to gain
automatic reclamation.

RAII is vital in writing exception-safe C++ code: to release resources before permitting exceptions to propagate
 (in order to avoid resource leaks) one can write appropriate destructors once rather than dispersing and duplicating
 cleanup logic between exception handling blocks that may or may not be executed.

RAII分类

可以依据资源可变性和资源来源这两个特征,来对RAII进行分类。

资源可变性

如果一个封装类对其实例提供额外的功能,使得其实例能被赋予新资源,这个类表现出的这种特征即称为"可变的RAII",否则就是"不可变的RAII"。

不可变的RAII,是使用起来最简单的一种。说它简单,是因为在这种情况下,无需在封装类中提供用于指定资源的方法--不管是新分配的资源,还是对其他资源进行拷贝。这种RAII还意味着,类的析构函数总是可以假定,被封装的资源是有效的。

与此相反,提供可变的RAII的类,就需要实现下列功能中的绝大部分,或者全部:缺省的或者空的构造函数,拷贝构造函数,拷贝赋值操作,用于指定资 源的方法。最重要的是,这样的类在析构函数和任何类似close()的方法中,释放资源前,都必须检测被封装的资源是不是null。

 

资源来源

对于提供RAII的类来说,第二个重要的特征是,它们通过什么途径获取自己所管理的资源。以std::string为代表的类,使用的是内部初始化 的RAII:它管理的资源--即内存中用于保存字符的缓冲区--是由它自己创建的,这一资源对外永远是不可见的。与此不同的是,以 std::auto_ptr为代表的类表现出外部初始化的RAII行为:它所管理的资源,是使用它的客户程序(通过另外的某种方式获得之后)交给它的。

内部初始化的RAII的封装类,一般比较容易实现,但是功能上也比较受限制,因为它们获取资源的机制是预先定义好的,并且是固定不变的。不过,这样的类用起来也容易一些,或者说,比较难被误用:因为客户代码几乎没有机会犯下能导致资源泄露的错误。

 

Resource management without RAII

Finalizers

In Java , objects are not allocated on the stack and must be accessed through references; hence you cannot have automatic variables of objects that "go out of scope". Instead, all objects are dynamically allocated . In principle, dynamic allocation does not make RAII unfeasible per se; it could still be feasible if there were a guarantee that a "destructor" ("finalize") method would be called as soon as an object were pointed to by no references (i.e., if the object lifetime management were performed according to reference counting ).

However, Java objects have indefinite lifetimes which cannot be controlled by the programmer, because, according to the Java Virtual Machine specification, it is unpredictable when the garbage collector will act. Indeed, the garbage collector may never act at all to collect objects pointed to by no references. Hence the "finalize" method of an unreferenced object might never be called or be called long after the object became unreferenced. Resources must thus be closed manually by the programmer, using something like the dispose pattern .

Reference Counting

Perl and CPython manage object lifetime by reference counting, making it possible to use RAII in a limited form. Objects that are no longer referenced are immediately released, so a destructor can release the resource at that time. However, object lifetime isn't necessarily bound to any lexical scope. One can store a reference to an object in a global variable, for example, thus keeping the object (and resource) alive indeterminately long. This makes it possible to accidentally leak resources that should have been released at the end of some scope. Also, in the case of Python, the actual garbage collection strategy is an implementation detail, and running with an alternative interpreter (such as IronPython or Jython) could result in the RAII implementation not working.

参考:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
http://www.cppblog.com/jinq0123/archive/2008/05/20/RAII.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值