Effective C++读书笔记 第三部分 资源管理

所谓资源就是,一旦用了它,将来必须还给系统。C++程序中最常使用的资源就好似动态分配内存(如果你new了,却忘了delete,会导致内存泄露),但内存只是你必须管理的众多资源之一。其它常见的有文件描述符(file descriptors)、互斥器(mutex)、图形界面中的字形和画刷。数据库连接以及网络sockets。当你不使用它们时,记得还给系统。

     当你考虑到异常、函数内多重回传路径、程序维护员改动软件却没能充分理解随之而来的冲击,那么资源管理就显得复杂的多。

条款13:以对象管理资源

本条款建议程序员使用对象管理资源(如申请的内存),给出的经验是:
(1) 为防止资源泄漏,请使用RAII(“资源取得时机便是初始化时机” (Resource Acquisition Is Initialization; RAII))对象,它们在构造函数中获得资源并在析构函数中释放资源。
(2) 两个常被使用的RAII classes 分别是trl: : shared_ptr 和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null。

例:
     
void f()
     { 
         Investment *pInv = createInvestment(); 
         ...                   //这里存在诸多“不定因素”,可能造成delete pInv;得不到执行,这可能就存在潜在的内存泄露。
         delete pInv;
     } 

    解决方法:把资源放进对象内,我们便可依赖C++的“析构函数自动调用机制”确保资源被释放。
    许多资源被动态分配于堆内而后被用于单一区块或函数内。它们应该在控制流离开那个区块或函数时被释放。标准程序库提供的auto_ptr正是针对这种形势而设计的特制产品。auto_ptr是个“类指针对象”,也就是所谓的“智能指针”,其析构函数自动对其所指对象调用delete。

void f()
     { 
         std::auto_ptr<Investment> pInv(createInvestment()); 
         ... 
     } 
         //函数退出,auto_ptr调用析构函数自动调用delete,删除pInv;无需显示调用delete。
    “以对象管理资源”的两个关键想法

  • 获得资源后立刻放进管理对象内(如auto_ptr)。每一笔资源都在获得的同时立刻被放进管理对象中。“资源取得时机便是初始化时机”(Resource Acquisition Is Initialization;RAII)。
  • 管理对象运用析构函数确保资源被释放。即一旦对象被销毁,其析构函数被自动调用来释放资源。
     由于auto_ptr被销毁时会自动删除它所指之物,所以 不能让多个auto_ptr同时指向同一对象。所以 auto_ptr若通过copying函数复制它们,它们会变成NULL,而复制所得的指针将取得资源的唯一拥有权!
     看下面例子:

 看下面例子:
    std::auto_ptr<Investment> pInv1(createInvestment()); //pInv1指向createInvestment()返回物;
     std::auto_ptr<Investment> pInv2(pInv1);                      //现在pInv2指向对象,而pInv1被设为NULL;
     pInv1 = pInv2;                                                               //现在pInv1指向对象,而pIn2被设为NULL;

    受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它即“有你没我,有我没你”。
    auto_ptr的替代方案是“引用计数型智能指针”(reference-counting smart pointer;SCSP)、它可以持续跟踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。
    TR1的tr1::shared_ptr就是一个"引用计数型智能指针"。
     void f()
     { 
         ... 
         std::tr1::shared_ptr
<Investment>  pInv1(createInvestment()); 
//pInv1指向createInvestment()返回物;
         std::tr1::shared_ptr<Investment>  pInv2(pInv1);                     //pInv1,pInv2指向同一个对象
         pInv1 = pInv2;                                                                          
//同上,无变化
         ... 
     } 
        //函数退出,pInv1,pInv2被销毁,它们所指的对象也竟被自动释放。
    auto_ptr和tr1::shared_ptr都在其析构函数内做delete而不是delete[],也就意味着在动态分配而得的数组身上使用auto_ptr或tr1::shared_ptr是个潜在危险,资源得不到释放。也许boost::scoped_array和boost::shared_array能提供帮助。还有,vector和string几乎总是可以取代动态分配而得的数组。

 请记住:

  • 为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
  • 两个常被使用的RAII类分别是auto_ptr和tr1::shared_ptr。后者通常是较佳选择,因为其拷贝行为比较直观。若选择auto_ptr,复制动作会使他(被复制物)指向NULL。   

条款14: 在资源管理类中小心 copying 行为

本条款提醒程序员,使用资源管理类时需根据实际需要管理copying行为,常见的有:抑制copying、施行引用计数法。

条款15: 在资源管理类中提供对原始资源的访问

(1) APIs往往要求访问原始资源( raw resources) ,所以每一个RAII class 应该提供一个”取得其所管理之资源”的办法。

(2) 对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换(如调用get()函数)比较安全,但隐式转换对客户比较方便。

条款16: 成对使用new 和delete 时要采取相同形式

本条款给出了程序员在申请和释放资源时常犯的错误,给出的经验是:

如果你在new 表达式中使用[],必须在相应的delete表达式中也使用[];如果你在new 表达式中不使用[],一定不要在相应的delete表达式中使用[]。

条款17: 以独立语句将newed 对象置入智能指针

本条款指出了一个使用智能指针时常犯的错误,避免该错误可以这样做:

以独立语句将newed 对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。举例:

processWidget(std::trl::sharedptr<W工dget> (new Widget) , priority());

在调用processWidget之前,编译器必须创建代码,做以下三件事:

(1) 调用priority

(2) 执行”newWidget”

(3) 调用trl:: shared_ptr 构造函数

不同的C++ 编译器执行这三条语句的顺序不一样,但对priority的调用可以排在第一或第二或第三执行。如果编译器选择以第二顺位执行且priority函数抛出了异常,则新创建的对象Widget将导致内存泄漏,解决方法如下:

std::trl::shared_ptr<Widget>pw(new Widget); //在独立语句内以智能指针存储Widget对象

processWidget(pw,priority()); //这个调用肯定不存在内存泄漏


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值