以对象管理资源------auto_ptr shared_ptr

来源:互联网 发布:linux vi 修改 编辑:程序博客网 时间:2024/05/16 13:00


例:

假设我们使用一个用来塑模投资行为(例如股票、债券等等)的程序库,其中各式各样的投资类型继承自一个root class Investment::
class Investment { ... };                         //"投资类型"继承体系中的root class

进一步假设,这个程序库系通过一个工厂函数(factory function ) 供应我们某特定的Investment 对象:
Investment* createlnvestment();          //返回指针,指向工nvestment继承体系内的动态分配对象。调用者有责任删除它。这里为了简化,                                                                 刻意不写参数。

createlnvestment 的调用端使用了函数返回的对象后,有责任删除之。现在考虑有个f 函数履行了这个责任:

void f( )

{
Investment* pInv = createlnvestment( );              //调用factory 函数

.....
delete plnv;                                                           //释放plnv 所指对象

}
    这看起来妥当,但若干情况下f 可能无法删除它得自createlnvestment的技资对象一一或许因为" ft 区域内的一个过早的return语句。如果这样一个return被执行起来,控制流就绝不会触及delete 语句。类似情况发生在对createlnvestment 的使用及delete 动作位于某
循环内,而该循环由于某个continue 或goto 语句过早退出。最后一种可能是“..... "区域内的语句抛出异常,果真如此控制流将再次不会幸临delete。无论delete如何被略过去,我们泄漏的不只是内含投资对象的那块内存,还包括那些投资对象所保存的任何资源。

    为确保createlnvestment返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。这里的想法是把资源放进对象内,我们便可倚赖C++ 的"析构函数自动调用机制"确保资源被释放。

一、    

     许多资源被动态分配于heap 内而后被用于单一区块或函数内。它们应该在控制流离开那个区块或函数时被释放。标准程序库提供的auto_ptr 正是针对这种形势而设计的特制产品。

   auto_ptr 是个"类指针(pointer-like) 对象",也就是所谓"智能指针",其析构函数自动对其所指对象调用delete。

下面示范如何使用auto_ptr 以避免f 函数潜在的资源泄漏可能性:

  void f ( ){
std::auto ptr<Investment> plnv(createlnvestment( )     //调用factory函数,一如以往地使用plnv,经由auto ptr 的析构函数自动删除plnv

......};  

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

1、获得资源后立刻放进管理对象(managing object) 内。以上代码中createlnvestment返回的资源被当做其管理者autoytr 的初值。实际上"以对象管理资源"的观念常被称为"资源取得时机便是初始化时机" (Resource Acquisition Is Initialization; RAII) ,因为我们几乎总是在获得一笔资源后于同一语句内以它初始化某个管理对象。有时候获得的资源被拿来赋值(而非初始化)某个管理对象,但不论哪一种做法,每一笔资源都在获得的同时立刻被放进管理对象中。
2、管理对象(managingobject) 运用析构函数确保资源被释放。不论控制流如何离开区块,一旦对象被销毁(例如当对象离开作用域)其析构函数自然会被自动调用,于是资源被释放。


注意:

由于auto ptr 被销毁时会自动删除它所指之物,所以一定要注意别让多个auto_ptr 同时指向同一对象。如果真是那样,对象会被删除→次以上,而那会使你的程序搭上驶向"未定义行为"的快速列车上。为了预防这个问题,auto_ptrs有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权!

std::auto_ptr<Investment>  plnvl(createlnvestment( ));           //plnvl指向createlnvestment返回物.
std::auto_ptr<Investment>  plnv2(plnvl);                         //现在plnv2 指向对象,plnvl 被设为null.
plnvl = plnv2;                                                                    //现在pInv1 指向对象,plnv2 被设为null.

STL 容器要求其元素发挥"正常的"复制行为,因此这些容器容不得auto_ptr。


二、

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

TRl 的trl::shared_ptr 就是个RCSP,所以你可以这么写f :

void f( ){

......
std::trl::shared ptr<Investment> plnv(createlnvestment( ));   //调用factory 函数,使用plnv一如以往,经由shared-ptr 析构函数自动删除plnv

......

}
这段代码看起来几乎和使用auto_ptr的那个版本相同,但shared_ptrs 的复制行为正常多了:

void f( )

{
std::trl::shared_ptr<Investment> plnvl(createlnvestment( ));    //plnvl指向createlnvestment返回物std::
trl::shared_ptr<Investment> plnv2(plnvl);                                  //plnvl 和plnv2 指向同一个对象
plnvl = plnv2;                                                                               //同上,无任何改变.

......

}                                                                                                 //plnvl 和plnv2 被销毁,它们所指的对象也就被自动销毁

由于trl: : sharedytrs 的复制行为"一如预期",它们可被用于STL 容器以及其他"auto_ptr 之非正统复制行为并不适用"的语境上。

auto_ptr 和trl::share_ptr 两者都在其析构函数内做delete 而不是delete[] 动作。

注意:

然而并非所有资源都是heap-based ,对那种资源而言,像auto_ptr 和trl: : share_ptr 这样的智能指针往往不适合作为资源掌管者(resource handlers) 。动态分配而得的array身上使用auto_ptr 或trl :shared_ptr 是个馊主意,你会惊讶地发现,并没有特别针对"C++ 动态分配数组"而设计的类似auto_ptr 或trl::share_ptr 那样的东西。


记住:

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

·两个常被使用的RAIl classes 分别是trl: : share_ptr 和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null。