资源就是一旦使用了它,将来必须还给系统。C++中最常用的资源就是动态分配的内存。
举个例子来说明我们面对的问题
一个用来模拟投资行为的程序库,其中各式各样的投资类型继承自一个root class Investment
class Investment {...}; //投资类型继承体系中的root class
它使用工厂模式供应我们某特定的Investment对象:
Investment * CreatInvestment(); //返回指针,指向一个动态分配对象,所以必须删除
void f()
{
Investment * pInv = CreatInvestment(); //调用函数
.... //一些操作
delete pInv //释放pInv所指对象
}
有很多原因造成delete pInv不能执行。
1) 例如在代码"...."中有一个return语句
2) 例如在代码"...."中发出一个异常等等
解决方法:用对象来管理资源
我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资。主要是依赖了对象的析构函数可以自动调用
应用举例:
编译器为我们提供一些这样的管理资源的类,同时你也可以自己编写。这里举两个编译器提供的类,下面的章节介绍如果自己设计需要注意的问题。
1) 类指针对象 auto_ptr 也就是智能指针,其析构函数自动对其所指对象调用delete. 例子:
void f()
{
std::auto_ptr<Investment >pInv(CreatInvestment()); //调用函数 并初始化pInv类指针对象
.... //和上面一样使用pInv
} //函数结束 经由auto_ptr的析构函数看自动删除pInv
auto_ptr被销毁时会自动删除它所指之物,所以一定别让多个auto_ptr同时指向一个对象,否则对象会被删除一次以上,这会引发程序的未定义形为。所以auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。例如:
std::auto_ptr<Investment >pInv1(CreatInvestment()); //pInv1指向一个investment对象
std::auto_ptr<Investment >pInv2(pInv1); //现在pInv2指向这个对象,pInv1被设为null
pInv1 = pInv2; //现在pInv1指向这个对象,pInv2被设为null
这个对复制操作的限制有局限性,下面的类指针可以避免这个问题
2) 引用计数型智慧指针(reference counting smart pointer RCSP)。它会追踪共有多少个对象指向某个资源,并在无人指向它时自动删除该资源。TRl的trl::shared_ptr就是个RCSP,所以你可以像使用std::auto_ptr一样使用,同时它对复制没有要求。
注意上面两个智能指针都在其析构函数内做delete而不是delete[]。所以对数组对象不能用这个指针。
总结:
1) 为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源
2) 两个常被使用的RAII classes分别trl::shared_ptr和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观,若选auto_ptr复制动作会使它(被复制物)指向null