本条款建议,如果你打算手工释放资源(例如使用delete
而非使用一个资源管理类; resource-managing class),容易发生某些错误。罐装式的资源管理类如auto_ptr
和 tr1 : :shared_ptr
往往比较能够轻松遵循本条款忠告。
void f( )
{
Investment* pInv = createInvestment ( ); //调用factory函数
...
delete pInv; //释放pInv所指对象
}
这看起来妥当,但若干情况下f可能无法删除它得自createInvestment
的投资对象-—或许因为"…”区域内的一个过早的return
语句。如果这样一个return
被执行起来,控制流就绝不会触及 delete
语句。类似情况发生在对createInvestment
的使用及 delete
动作位于某循环内,而该循环由于某个continue
或goto
语句过早退出。最后一种可能是…”区域内的语句抛出异常,果真如此控制流将再次不会幸临delete
。
无论delete
如何被略过去,泄漏的不只是内含投资对象的那块内存,还包括那些投资对象所保存的任何资源。可能会造成添加return
语句或continue
语句而未能全然领悟它对函数的资源管理策略造成的后果,或者在"…”区域有可能调用一个“过去从未抛出异常,却在被“改善”之后开始那么做”的函数。
因此单纯依赖“f
总是会执行其delete
语句”是行不通的。
做法:
为确保createInvestment
返回的资源总是被释放,需要将资源放进对象内,当控制流离开f
,该对象的析构函数会自动释放那些资源。便可倚赖C++的“析构函数自动调用机制”确保资源被释放。
许多资源被动态分配于heap
内,而后被用于单一区块或函数内。他们应该在控制流离开那个区块或函数时被释放。auto_ptr
正是针对这种形式而设计的。
auto_ptr
是个“类指针对象”,也即是智能指针,其析构函数自动对其所指对象调用delete
,一定要注意别让多个auto_ptr
同时指向同一对象。
下面示范auto_ptr
如何使用以避免f
函数潜在的资源泄漏可能性:
void f( )
{
std::auto_ptr<Investment> pInv = (createInvestment() ); //调用factory函数
... //一如既往地使用pInv,经由auto_ptr的析构函数自动删除pInv
}
这个例子示范“以对象管理资源”的两个关键想法:
1、获得资源后立刻放进管理对象内,“以对象管理资源”也被称为“资源取得时机就是初始化时机”(RAII);
2、管理对象运用析构函数确保资源被释放。
auto_ptr
性质:若通过copy
构造函数或copy assignment
操作符复制它们,它们会变成null
,而复制所得的指针将取得资源的唯一拥有权!
必须指出,createInvestment
返回的“未加工指针”,简直是对资源泄漏的一个死亡邀约,因为调用者极易在这个指针身上忘记调用delete
。(即使他们使用auto_ptr
或tr1::shared ptr
来执行delete
,他们首先必须记得将createInvestment
的返回值存储于智能指针对象内。)为与此问题搏斗,首先需要对createInvestment
进行接口修改,条款18将分析。
总结:
为防止资源泄露,请使用RAII对象,他们在构造函数中获得资源并在析构函数中释放资源。
两个常使用的RAII
classes
分别是tr1::shared_ptr
和auto_ptr
。前者通常是较好选择,因为其copy
行为比较直观。若选择auto_ptr
,复制动作会使它指向null
。