条款13:以对象管理资源
此条款,针对对象管理,内容分为三个部分:
- 为什么用类管理
- 怎么用类管理
- 有什么要注意的
一、为什么用类管理
解释为什么用,可以用一个反例来衬托用类管理的好,即一个原书中的例子:
class Investment{ ... } //base class
//Factory(工厂)函数
Investment* createInvestment(); // 返回指针,指向Investment继承体系内的动态分配对象
// 调用者有责任删除它
void f()
{
Investment* pInv = createInvestment(); // 调用factory函数
...
delete pInv; // 释放pInv所指的对象
}
这种程序设计的缺陷主要在于我们可能无法释放获取的pInv对象:
- 如果在delete之前有return语句导致函数执行结束,那么对象就无法释放。
- 如果在delete之前程序抛出异常,那么也无法释放对象。
- 如果这段代码在之后软件开发维护过程中被修改,那么后人可能无法知道要释放这个pInv对象,因为单纯的靠函数f中的delete语句来释放对象是行不通的。
二、怎么用类管理
用类管理的核心思想就是RAII,即 Resources Acquisition Is Initialzeation,指资源取得的时机便是初始化时机,重点是自动处理资源 。
具体处理思路为获得资源后立刻放进管理对象内,使用类性质达到自动处理资源的效果,确保管理对象能运用析构函数使得资源被释放,其代表就是智能指针(auto_ptr 和shared_ptr)。
但是如果析构函数抛出异常,可能需要自己手动处理,参考条款8的析构异常处理。
C++程序库提供了两种类,更加安全的管理自己的资源,分别是:shared_ptr和auto_ptr(现在它已经被unique_ptr替代)。
1、auto_ptr
auto_ptr是个智能指针,其析构函数自动对其所指对象调用delete,但此智能指针是存在唯一性的,即赋值操作后智能指针失效,保证同时最多一个智能指针指向管理对象。
void f()
{
//调用函数获得对象
std::auto_ptr<Investment> pInv(createInvestment());
...
}//函数执行完之后,auot_ptr的析构函数自动删除pInv
//获得一个对象,并管理该对象
std::auto_ptr<Investment> pInv1(createInvestment());
//拷贝pInv1,此时pInv1被设为null,现在pInv2管理这个资源
std::auto_ptr<Investment> pInv2(pInv1);
//赋值操作,此时pInv2变为null,现在pInv1管理这个资源
pInv1 = pInv2;
2、shared_ptr
shared_ptr也是智能指针,但是其与autp_ptr不同,其是“引用计数型智能指针”(RCSP),也就是多个shared_ptr可以同时指向一个管理对象,只有当没有shared_ptr指向管理对象(引用计数为0),才会销毁。
三、有什么要注意的
C++没有为动态分配数组而设计的智能指针类,是因为C++已经有了vector和string这样的管理类,这些类已经够我们使用了,因此提供动态分配数组管理的类是多余的
但是Boost库中的boost::scoped_array和boost::shared_array类是接近于为数组而设计的类,因此你们想要了解的话,可以参阅这两个类。
//都是错误的
std::auto_ptr<std::string> aps(new std::string[10]);
std::shared_ptr<int> aps(new int[1024]);
四、总结
为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源
两个常被使用的RAII类分别是tr1::shared_ptr和auto_ptr(以抛弃不再使用)
shared_ptr通常是较佳选择,因为其拷贝行为比较直观
auto_ptr复制动作会使它(被复制对象)指向null