所谓资源就是,一旦用了它,将来必须还给系统。
假设我们使用一个用来塑模投资行为的程序库,其中各式各样的投资类型继承自一个root class Investment:
- class Investment{...};//root class
进一步假设,这个程序库通过一个工厂函数提供我们某特定的Investment对象:
- Investment* createInvestment();//反向指针,指向Investment继承体系内
- //的动态分配对象。调用者有责任删除它。
- //这里为了简化,特意不写参数
createInvestment的调用端使用了返回的对象后,有责任删除之。
- void f()
- {
- Investment* pInv = createInvestment();//调用工厂函数
- ...
- delete pInv;//释放pInv所致对象
- }
这看起来妥当,但若干情况下f可能无法删除它得自createInvestment的投资对象。如果在delete之前有return被执行,那对象不会被delete。
为了确保createInvestment的返回资源总是被释放,我们需要将资源放进对象内,当控制流离开f,该对象的析构函数会自动释放那些资源。把资源放进对象内,我们便依赖C++的“析构函数自动调用机制”确保资源被释放。
许多资源被动态分配在heap内而后被用于单一区块或函数内。它们应该在控制流离开那块区块或函数时被释放。标准程序库提供的auto_ptr正是针对这种形式而设计的。auto_ptr是个“类指针对象”,也就是所谓的智能指针,其析构函数自动对其所指对象调用delete。下面示范如何使用auto_ptr以避免f函数潜在的资源泄露可能性:
- void f()
- {
- std::auto_ptr<Investment> pInv(createInvestment());
- //调用工厂函数
- ... //一如既往的使用pInv
- } //经由auto_ptr的析构函数自动删除pInv
这个简单的例子示范“以对象管理资源”的两个关键想法:
1.获得资源后立即放进管理对象内。
以上代码中createInvestment返回的资源被当作其管理者auto_ptr的初值。获得一个资源后于同一语句内以它初始化某个管理对象。有时候资源获得后被拿来赋值某个管理对象。每一个资源都在获得的同时立即被放进管理对象中。
2.管理对象运用析构函数确保资源被释放
不论控制流如何离开区块,一点对象被销毁其析构函数自然会被自动调用,以上资源被释放。
由于auto_ptr被销毁时会自动删除它所指物,所以一定要注意别让多个auto_ptr同时指向同一个对象。
为了预防这个问题,auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符赋值它们,它们会变成null,而复制所得的指针将取的资源的唯一拥有权。
- std::auto_ptr<Investment> pInv1(createInvestment());
- //pInv1指向createInvestment返回物
- std::auto_ptr<Investment> pInv2(pInv1);//pInv2指向对象,pInv1被设为null
- pInv1 = pInv2;//现在pInv1指向对象,pInv2被设为null
这一诡异的赋值行为,附加上其底层条件:“受auto_ptr管理的资源必须绝对没有一个以上的auto_ptrtonsil指向它”,意味auto_ptr并非管理动态分配资源的最好方法。STL容器要求其元素发挥正常的复制行为,因此这些容器不得auto_ptr。
auto_ptr的替代方案是“引用计数型智能指针(RCSP)”。RCSP也是个智能指针,持续追踪共有多少对象指向某个资源,并在无人指向它时自动删除该资源。RCSP提供的行为为类似垃圾回收,不同的是RCSP无法打破环状引用(例如两个其实已经没被使用的对象彼此互指,因而好像还处在“被使用”状态)。
TR1的tr1::shared_ptr就是个RCSP,所有你可以这么写f:
- void f()
- {
- ...
- std::tr1::shared_ptr<Investment> pInv(createInvestment());
- //调用工厂函数
- .....//使用pInv
- }//经由shared_ptr析构函数自动删除pInv
这段代码看起来几乎和使用auto_ptr一样,但是shared_ptr的复制行为正常多了:
- void f()
- {
- ...
- std::tr1::shared_ptr<Investment> pInv1(createInvestment());
- //pInv1指向资源
- std::tr1::shared_ptr<Investment> pInv2(pInv1);;
- //pInv1和pInv2指向同一个对象
- pInv1 = pInv2; //同上
- ...
- } //pInv1和pInv2被销毁,它们所指的对象也就被自动销毁
tr1::shared_ptr可被用于STL已经其他语境上。
atuo_ptr和tr1::shared_ptr两者都在其析构函数内做delete而不是delete[]动作,那意味在动态分配而得的数组身上使用auto_ptr和tr1::shared_ptr不是个好主意。但是,如果用了,仍能编译通过。
1.为防止资源泄露,请示使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
2.两个常被使用的RAII类分别是tr1::shared_ptr和auto_ptr。前者通常是较佳的选择,因为copy行为比较直观,若选择auto_ptr,复制动作会使它指向NULL。