《Effective C++》Item13:使用RAII对象管理资源

所谓的“资源”,指的就是那些“一旦使用,将来必须要归还给操作系统”的组件。在C++程序当中,最常使用的资源就是堆内存;然而内存只是我们必须管理的众多资源之一,其它常用的资源还包括:文件描述符线程互斥量网络连接等等。无论是哪一种资源,在使用完毕后都必须要归还给服务提供者。

尝试在任何情况下都手工维护资源,实际是一件很困难的事。因为一旦引入了多种执行流程序异常等特性,那么手中的资源就很容易失去控制。

假使我们使用一个用来抽象投资行为的类库,其中各种各样的投资类型都继承自同一个基类Investment

class Investment { /* ... */ };

进一步,这个程序库通过一个工厂函数来提供某个特定的Investment对象:

//此函数返回指针,
//指向一个Investment体系内的对象。调用者有责任手工删除它。 
Investment *createInvestment();                    

如代码中的注释所言,工厂函数createInvestment的调用者有责任手工删除掉获取到的对象。现在假设在业务逻辑当中,有一个函数function调用了这个工厂函数,并进行了删除操作:

void function( /* ... */ )
{
    Investment *invest = createInvestment();
    //...
    delete invest;
}

目前看起来程序工作良好,但是在很多情况下,函数function无法删除这个对象:

  • 可能在中间的某个逻辑中,函数认为功能已经完成,因此直接让函数返回,invest指向的对象就来不及释放;
  • 可能在中间的某个逻辑中,函数抛出了异常,则程序永远不会执行到delete语句。

无论delete语句是如何被忽略的,我们浪费掉的不仅仅是单一的一个Investment的子类对象,还包括这个对象内部包含的所有对象。

当然,精心地安排逻辑实现(例如在任何的一个代码分支中都提供一个相同的delete语句),可以防止这一错误;但是其一,它造成了重复的代码;其二,在未来的更新迭代过程当中,可能会添加类似return这样的语句,而全然未意识到它对资源管理造成的影响。因此,单纯地依赖开发者的资源管理意识和经验,这显然是不靠谱的。

为了确保createInvestment工厂函数返回的对象资源总是可以被正确地回收,我们需要将这个资源放入一个对象当中:当控制流离开了函数后,这个对象的析构函数就自动地回收这个资源。也就是说:把资源放进对象内,就可以依赖C++的“析构函数离开作用域时将被自动调用”这一机制,确保资源被释放。

许多资源被动态分配在堆内存中,并仅在某一个作用域中使用;这些堆内存应该在离开这个作用域时立刻被回收。STL提供了auto_ptr来完成这项工作,它的析构函数自动对其所指的对象调用delete

void function()
{
    std::auto_ptr<Investment> invest(createInvestment());

    //...
}

这个例子实际上示范了“以对象管理资源”的两个关键思路:

  • 获得资源后立刻放进管理对象。以上代码中createInvestment返回的资源被当作资源管理者auto_ptr的初值;而实际上“以对象管理资源”的观念常被称为“资源获取即初始化”(RAII)。因为我们几乎总是在获得某个资源后,在同一条语句内就创建出这个管理对象。
  • 管理对象使用析构函数来确保资源被释放。无论控制流以怎样的方式结束当前作用域,一旦管理对象被销毁,那么其析构函数就自然会被自动调用,于是资源会被安全地回收。如果资源的回收过程中可能抛出异常,那么问题会变得有些棘手,但是条款8已经能够解决这个问题。

接下来原书中介绍了以上思想的两个实例:shared_ptrunique_ptr

【注意】
为了放置资源泄露,请使用RAII对象来管理资源的生命周期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值