【Effective c++】条款13:以对象管理资源

来源:互联网 发布:c语言打印字母图形 编辑:程序博客网 时间:2024/05/01 07:11

所谓资源就是,一旦用了它,将来必须还给系统。

 

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

  1. class Investment{...};//root class 

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

  1. Investment* createInvestment();//反向指针,指向Investment继承体系内 
  2.                                //的动态分配对象。调用者有责任删除它。 
  3.                                //这里为了简化,特意不写参数 

createInvestment的调用端使用了返回的对象后,有责任删除之。

  1. void f() 
  2.   Investment* pInv = createInvestment();//调用工厂函数 
  3.   ... 
  4.   delete pInv;//释放pInv所致对象 

这看起来妥当,但若干情况下f可能无法删除它得自createInvestment的投资对象。如果在delete之前有return被执行,那对象不会被delete。

为了确保createInvestment的返回资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。把资源放进对象内,我们便依赖C++的“析构函数自动调用机制”确保资源被释放。

许多资源被动态分配在heap内而后被用于单一区块或函数内。它们应该在控制流离开那块区块或函数时被释放。标准程序库提供的auto_ptr正是针对这种形式而设计的。auto_ptr是个“类指针对象”,也就是所谓的智能指针,其析构函数自动对其所指对象调用delete。下面示范如何使用auto_ptr以避免f函数潜在的资源泄露可能性:

  1. void f() 
  2.   std::auto_ptr<Investment> pInv(createInvestment()); 
  3.              //调用工厂函数 
  4.     ...      //一如既往的使用pInv 
  5. }            //经由auto_ptr的析构函数自动删除pInv 

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

1.获得资源后立即放进管理对象内。

以上代码中createInvestment返回的资源被当作其管理者auto_ptr的初值。获得一个资源后于同一语句内以它初始化某个管理对象。有时候资源获得后被拿来赋值某个管理对象。每一个资源都在获得的同时立即被放进管理对象中。

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

不论控制流如何离开区块,一点对象被销毁其析构函数自然会被自动调用,以上资源被释放。

 

由于auto_ptr被销毁时会自动删除它所指物,所以一定要注意别让多个auto_ptr同时指向同一个对象。

为了预防这个问题,auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符赋值它们,它们会变成null,而复制所得的指针将取的资源的唯一拥有权。

  1. std::auto_ptr<Investment> pInv1(createInvestment()); 
  2.                   //pInv1指向createInvestment返回物 
  3. std::auto_ptr<Investment> pInv2(pInv1);//pInv2指向对象,pInv1被设为null 
  4.  
  5. pInv1 = pInv2;//现在pInv1指向对象,pInv2被设为null 

这一诡异的赋值行为,附加上其底层条件:“受auto_ptr管理的资源必须绝对没有一个以上的auto_ptrtonsil指向它”,意味auto_ptr并非管理动态分配资源的最好方法。STL容器要求其元素发挥正常的复制行为,因此这些容器不得auto_ptr。

auto_ptr的替代方案是“引用计数型智能指针(RCSP)”。RCSP也是个智能指针,持续追踪共有多少对象指向某个资源,并在无人指向它时自动删除该资源。RCSP提供的行为为类似垃圾回收,不同的是RCSP无法打破环状引用(例如两个其实已经没被使用的对象彼此互指,因而好像还处在“被使用”状态)。

TR1的tr1::shared_ptr就是个RCSP,所有你可以这么写f:

  1. void f() 
  2.   ... 
  3.   std::tr1::shared_ptr<Investment> pInv(createInvestment()); 
  4.                           //调用工厂函数 
  5.   .....//使用pInv 
  6. }//经由shared_ptr析构函数自动删除pInv 

这段代码看起来几乎和使用auto_ptr一样,但是shared_ptr的复制行为正常多了:

  1. void f() 
  2.   ... 
  3.   std::tr1::shared_ptr<Investment> pInv1(createInvestment()); 
  4.                    //pInv1指向资源 
  5.   std::tr1::shared_ptr<Investment> pInv2(pInv1);; 
  6.                   //pInv1和pInv2指向同一个对象 
  7.   pInv1 = pInv2;  //同上 
  8.   ... 
  9. }  //pInv1和pInv2被销毁,它们所指的对象也就被自动销毁 

tr1::shared_ptr可被用于STL已经其他语境上。

atuo_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[]动作,那意味在动态分配而得的数组身上使用auto_ptr和tr1::shared_ptr不是个好主意。但是,如果用了,仍能编译通过。

 

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

2.两个常被使用的RAII类分别是tr1::shared_ptr和auto_ptr。前者通常是较佳的选择,因为copy行为比较直观,若选择auto_ptr,复制动作会使它指向NULL。


原创粉丝点击