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的析构函数自动删除pInv1.获得资源后立即放进管理对象(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的析构函数自动删除pInvshared_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行为比较直观。