C++内存管理------>以对象管理资源(Effective C++)

来源:互联网 发布:电子商务网络推广 编辑:程序博客网 时间:2024/06/05 17:02

考虑如下一个类:

class Investment{...};

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

Investment* createInvestment();//返回指针,指向Investment继承体系内的动态分配对象。调用者有责任删除它。

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

void f(){  Investment* pInv=createInvestment();//调用工厂函数  ...  delete pInv;//释放pInv所指对象}

这看起来妥当。但若干情况下 f 可能无法删除它得createInvestment的投资对象。或许因为“….”区域内的一个过早的return语句。如果这样的return 语句被执行起来,控制流就绝不会触及delete语句类似情况发生在对createInvestment的使用及delete动作位于某循环内,而该循环由于某个continue或者go语句过早退出。最后一种可能是“…..”区域内的语句抛出异常,果真如此控制流将再次不会临delete。无论delete如何被略过去,我们泄露的不只是内含投资对象的那块内存,还包括那些投资对象所保存的任何资源。
为确保createInvestment返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。即:把资源放进对象内,我们便可倚赖C++的“析构函数自动调用机制”确保资源被释放。
许多资源被动态分配于heap内而被用于单一区块或函数内。它们应该在控制流离开那个区块或函数时被释放。标准程序库提供的auto_ptr正是针对这种形式而设计的特制产品。auto_ptr是个“类指针对象”。也就是所谓的“智能指针”,其析构函数自动对其所指对象调用delete。下面示范如何使用auto_ptr以避免f函数潜在的资源泄露可能性:

void f(){  std::auto_ptr<Investment> pInv(createInvestment());                                      //调用工厂函数                                      //继续使用pInv                                      //经由auto_ptr的析构函数自动删除pInv  ...}

以对象管理资源的两个关键想法:

一、获得资源后立刻放进管理对象

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

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

std::auto_ptr<Investment> pInv1(createInvestment());//pInv1指向createInvestment返回物  std::auto_ptr<Investment> pInv2(pInv1);//现在pInv2指向对象,pInv1被设为NULL  pInv1=pInv2;//现在pInv1指向对象,pInv2被设为NULL

由此可见auto_ptr并非管理动态分配资源的神兵利器。举个例子,STL容器要求其元素发挥正常的复制行为,因此这些容器容不得auto_ptr

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

TR1的tr1::shared_ptr就是个RCSP,所以你可以这么写f:
void f(){   ...   std::tr1::shared_ptr<Investment>  pInv(createInvestment());   //调用工厂函数   //使用pInv一如以往   ...}//经由shared_ptr析构函数自动删除pInv

这段代码与auto_ptr那个版本相同,但是shared_ptr的复制行为正常多了。

void f(){   ...   std::tr1::shared_ptr<Investment> pInv1(createInvestment());//pInv1指向createInvestment返回物   std::tr1::shared_ptr<Investment> pInv2(pInv1);//pInv2和pInv1指向同一个对象   pInv1=pInv2;//同上,无任何改变   ...}//pInv2和pInv1被销毁,它们所指的对象也就被自动销毁

auto_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[ ]动作。那意味着在动态分配而得的array身上使用auto_ptr或tr1::shared_ptr是个馊主意。尽管如此,这么做仍然能通过编译!

std::auto_ptr1<std::string> aps(new std::string[10]);//这么做是错的,会用上错误的delete形式std::tr1::shared_ptr<int> spi(new int[1024]);//同上
1 0