effective C++ 条款 13:以对象管理资源

资源是,一旦用了,将来必须还给系统,除了内存常见的还有文件描述符(file description)、互斥锁、图形界面中的笔刷、数据库连接

以及网络socket。考虑到异常、函数内多重回传路径、程序维护员软件改动却没有理解随之而来的冲击,发现资源管理的特殊手段还不很充分够用。

假设一个各式各样的投资类型继承的一个root class Investment:

class Investment {...};

假设通过一个工厂函数供应我们某特定的Investment对象:

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

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

若果…中有一个过早的return语句,或者抛出异常,那么控制流将不会幸临delete。

为确保资源总是被释放,我们需要将资源放进对象内,这样便可依赖c++的“析构函数自动调用机制”确保资源被释放。标准程序库提供的auto_ptr正是针对这种形势设计的特制产品。auto_ptr是一个“类指针(pointer-like)对象”:

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

1.获得资源后立即放进管理对象(managing object)内。createInvestment返回的资源被当做其管理者auto_ptr的初值。

“以对象管理资源”的观念常被称为“资源取得时便是初始化时”(resource Acquisition is Initialization;RAII),有时获得的资源拿来赋值(而非初始化)某个管理对象,但不论哪种做法,每笔资源在获得的同时立刻被放进管理对象中。

2.管理对象运用析构函数确保资源被释放。一旦对象被销毁(当对象离开作用域),其析构函数自然会自动调用,于是资源被释放。

由于autu_ptr被销毁时会自动删除它所指之物,所以一定不要让多个auto_ptr同时指向同一个对象。为防止这个问题,auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针取得资源的唯一使用权。

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

这个诡异的复制行为,使得要求其元素发挥“正常的”复制行为的像stl容器这种容不得auto_ptr。

auto_ptr的替代方案是“引用计数型智慧指针”(reference-counting smart pointer;RCSP)PCSP持续追踪共有多少对象指向

某笔资源,并在无人指向它时删除该资源。类似于垃圾回收,但无法打破环状引用(cycle of reference,例如两个其实已经没有被使用的对象彼此互指,因而好像在“被使用”状态)。

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

void f()
{
    std::tr1::shared_ptr<Investment> pInv(createInvestment());
    ...
}//经由shared_ptr的析构函数自动删除pInv

shared_ptr的复制正常多了

void f()
{
    std::tr1::shared_ptr<Investment> pInv1(createInvestment());
    std::tr1::shared_ptr<Investment> pInv2(pInv1);//pInv1和pInv2指向同一个对象
    pInv1 = pInv2;
    ...
}//pInv1和pInv2被销毁,他们指向的对象也就被销毁。

所以shared_ptr可以被用于stl容器及其他“auto_ptr之非正统复制行为并不合适”的语境上。

auto_ptr及shared_ptr在其析构函数内做delete而不是delete[]动作。那意味着在动态分配而得的array身上使用auto_ptr或shared_ptr是个馊主意。因为vector和string几乎总是可以替代动态分配而得的数组。boost::scope_array 和boost::shared_array classes是针对数组设计的类似auto_ptr和tr1::shared_ptr那样的类。

防止资源泄漏,请使用RAII对象,他们在构造函数中获得资源并在析构函数中释放资源。shared_ptr比auto_ptr是个较佳的选择

因为其copy行为比较直观。

posted @ 2012-01-14 10:56  lidan  阅读(291)  评论(0编辑  收藏  举报