您的位置:首页 > 编程语言 > C语言/C++

【Effective c++】条款13:以对象管理资源

2012-08-30 10:38 381 查看
所谓资源就是,一旦用了它,将来必须还给系统。

假设我们使用一个用来塑模投资行为的程序库,其中各式各样的投资类型继承自一个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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: