条款13:以对象管理资源

所谓有资源,就是你一旦使用了它,就要记得归还系统。如果不这样做,就会发生资源的浪费。这里的资源不仅仅是指内存,也包括:文件描述符,互斥锁,数据库连接…

本条款是一个直接而易懂的基于对象的资源管理方法,建立在C++对构造函数,析构函数,copy函数的基础上。经验显示,经过训练后严守这些做法,可以几乎消除资源管理的问题。下面我们就开始了~~~~~

 

首先,我们以工厂模式来说起:

假如我们使用一个用来模拟投资行为的程序库,其中各式各样的投资类型继承自一个root class Investment;

classInvestment{};

进一步假设,这个程序库是通过一个工厂函数供应我们某特定的Investment对象:

Investment* createInvestment(){}//返回指针,指向Investment继承体系内的动态分配对象。调用者有责任删除它,这里为了简化,不写参数。

 

现在考虑有个f函数履行了这个责任:

voidf(){

       Investment*pInv=createInvestment();

       ...

       deletepInv;

}

这似乎看起来很美好,但是不幸的事情发生了,它发生在“…”代码中,可能是return了,也可能是出现了异常,总之,没有执行到delete上。我们泄露的不只是那些内存块,还有那些投资对象所保存的任何资源。

 

现在的问题是pInve和delete不在同一个对象,如果它们在同一个对象,我们完全可以依靠对象的析构函数来进行析构。因此……..

我们可以用标准库提供的auto_ptr。这是一个类指针对象。也就是所谓的智能指针。其析构函数自动对其所指对象调用delete。

voidf(){

       std::auto_ptr<Investment>pInv(createInvestment());

       ...          

}

 

这个简单的例子示范“以对象管理资源”的两个关键想法:

l  获得资源后,立刻放进管理对象内。

在以上代码中,createInvestment返回的资源被当作其管理者auto_ptr的初值。实际上“以对象管理资源”的观念常被称为“资源获取时机便是初始化时机”(Resource Acquisition Is Initializatioin, RAII).

l  管理对象运用析构函数确保资源被释放。

 

由于auto_ptr被销毁时,会自动删除它所指之物,所以一定要注意别让多个auto_ptr同时指向同一个对象。如果真是那样,对象会被删除一次以上,会造成未定义行为。为了预防这个问题,auto_ptr有一个不寻常的性质:若通过copy函数复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。如下:

std::auto_ptr<Investment>pInv(createInvestment());//pInv point to a Investment obj

std::auto_ptr<Investment>pInv2(pInv);//pInv2 point to obj, pInv is null

pInv=pInv2();//pInv piont to obj, pInv2 is null

这一诡异的复制行为,复加上其底层乱条件:“受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它”。但是在STL中,容器要求其元素发挥“正常的”复制行为,因此这些容器不得auto_ptr。

 

Auto_ptr的一个替代方案就是“引用计数型智能指针(Reference-counting smart pointer, RCSP)”。所谓的RCSP也是一个智能指针。持续追踪共有多少个对象指向某笔资源,并在无人指向它时,自动删除该资源。

voidf()

{

         std::tr1::shared_ptr<Investment>pInv1(createInvestment());//pInv1 point to a Investment obj

         std::tr1::shared_ptr<Investment>pInv2(pInv1);//pInv1 and pInv2 point to the same obj;

         pInv1=pInv2;//pInv1 and pInv2 point to the same obj;

}

 

Auto_ptr 和trl::shared_ptr两者都在其析构函数内做delete而不是delete[]动作。那意味着在动态分配而得到的array身上使用Auto_ptr 和trl::shared_ptr是个坏主意。

 

请记住:

l   为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。

l   两个常使用的RAII classes 分别是Auto_ptr 和trl::shared_ptr。后者是一个较佳的选择。因为其copy行为比较直观。若选择前者,复制动作会使它指向null。

posted @ 2012-11-15 22:33  loveyakamoz  阅读(376)  评论(0编辑  收藏  举报