《Effective C++》读书笔记之中资源管理

                                                                                                                                By Ryui Liu 2011/11/28

所谓资源就是一旦用了它,将来必须还给系统,如果不这样,糟糕的事情就会发送,如内存泄漏等等,c++中最常使用的资源就是动态内存,但内存只是必须管理的资源之一,其他常见资源如文件描述器(file descriptors)、互斥锁、图形界面中的字型和笔刷、数据库连接以及网络sockets等等。不论那种资源,当你不使用它时,必须将它还给系统。

一、以对象管理资源:

    假设我们有一个root class.

      Class Investment{.....};

然后通过一个工场函数供应特定的Investment继承体系内的对象。

      Investment * createInvestment();

一般情况下我们使用方法都是

      Investment *pInv = createInvestment();

      .....

      delete pInv;

这样看起来毫无问题,但是当有人需要变更程序,在“...”处加入流控制,如return 语句、goto语句,此时就可能导致跑不到delete pInv语句。

为确保createInvestment返回的资源总是被释放,我们就需要把资源放进管理对象内,这些对象的析构函数会自动释放那些资源。

std::auto_ptr<Investment> pInv(createInvestment()); //调用factory函数 创建管理对象pInv

auto_ptr是“类指针对象”,也就是所谓的智能指针,其析构函数自动对其所指对象调用delete

到此为止,可以总结“以对象管理资源”的关键:

1、获得资源后立刻放进管理对象(managing object)内:以上代码中createInvestment返回的资源被当作其管理者auto_ptr的初值。“以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”(Resource Acquisition is Initiali RAII,资源管理对象也称为RAII对象  

2、管理对象运用析构函数确保资源被释放:不论控制流如何离开区块,一旦对象被销毁,其析构函数自然会被自动调用,于是资源被释放。

   但是auto_ptr有个特性:受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向他。因此在调用copy构造函数或copy assignment操作符复制它们的时候,原管理器会变成null,而复制所得的指针将取得资源的唯一管理权。在STL中要求其元素有正常的复制行为,因此这些容器容不得auto_ptr,由此可见auto_ptr并非管理资源的神兵利器。

Std::auto_ptr<Investment> pInv1(pInv);//此时pInv被设为null;

pInv = pInv1; //此时pInv1被设为null;   

auto_ptr的替代方案是“引用计数型智慧指针(reference-counting smart pointer: RCSP)”。RCSP将持续追踪有多少个对象指向某资源,当无人指向它是会自动删除该资源。TR1tr1::share_ptr就是个RCSP,其复制行为比较正常。

二、资源管理类中copying行为:

    下面有一个类对类型为Mutex的互斥对象进行管理

Class Lock{

  Public: 

     explicit Lock(Mutex *pm):mutexptr(pm)

     {  lock(mutexPtr);}

     ~Lock(){ unlock(mutexPtr)}

  Private:

     Mutex * mutexPtr;  

}

此时调用构造函数时会锁定互斥器,在区块末尾时,自动接触互斥器锁定。

但是当Lock对象被复制时:Lock m11(&m);  Lock ml2(ml1); 这时会怎么样?

“当一个RAII对象被复制时,会发生什么?”,我们有如下两种选择

1、禁止复制: 当允许RAII对象被复制时并不合理,此时需要禁止复制,即将coping操作声明为私有。

2、对底层资源使用“引用计数法”:

上面的示例中就可以将Mutex* 变为std::tr1::shared_ptr<Mutex> mutexPtr; 并将tr1::shared_ptr的“删除器”指定为unlock函数,这样在引用计数为0时,解除锁定,而不是删除对象。构造函数改为:

 explicit Lock(Mutex *pm): mutexPtr(pm,unlock) { lock(mutexPtr.get());}

3、复制底部资源:当希望一份资源可以有任意数量的复件时,可以复制底部资源。例如某些标准字符串类型是由“指向heap内存”的指针构成。这种字符串对象还含有指针指向的一块heap内存,当这样的字符串被复制时,不论指针还是指针指向的内存都会被制作出一个复件。

4、转移底部资源的拥有权auto_ptr 就是这么处理的。

   三、在资源管理类中提供对原始资源的访问:

   有时候需要绕过资源管理器对象直接访问原始资源,例如一个函数int daysHeldconst Investment *pi)需要直接访问Investment指针,而现在只有tr1::share_ptr<Investment>的对象。这时候就需要一个函数将RAII class对象转换为其所包含的原始资源。

1、显式转换: tr1::shared_ptrauto_ptr都提供了一个get成员函数,用来显示转换,他返回智能指针内部的原始指针(的附件)。 Int daysHeld(pInv.get());

2、隐式转换: 如果需要用原始指针的函数,智能指针可以直接通过操作符->*进行引用,因为智能指针重载了取值操作符,运行隐式转换为底部原始指针。这里的隐式转换是从类类型转换为成员变量类型,不同于无explicit声明的单参数构造函数将成员变量类型转换为类类型

示例:FontHandle getFont();

      FontHandle releaseFont();

 资源管理类

      class Font{

         public:

           //C++中单参数构造函数若不声明为explict,

           //在合适的场合可以产生隐式转换:由成员变量类型转换为类类型。 

           explicit Font(FontHandle fh):f(fh){}

           ~Font(){releaseFont(f);}

           FontHandle get()const {return f;}//显示转换函数

           //可以提供一个隐式转换函数,由类类型转换为成员变量类型

           Operator FontHandle()const{ return f;}

         Private:

            FontHandle f;

      }

四、以独立语句将new操作符创建的对象置入智能指针,如果不这么做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。例如:

       int priority();

       void processWidget(std::trl::shared_ptr<widget> pw, int priority);

调用上面函数时:

        processWidget(std::trl::shared_ptr<widget>(new Wigdet), priority());

上面的调用看似不会出现内存泄漏,实则不然:C++编译器调用之前必须先处理实参,由于c++编译器的不确定性,可能产生的操作顺序为:

        1、执行"new Widget" 2、调用 priority 3、调用 trl::shared_ptr构造函数。

假如上述过程第2步产生异常,此时"new Widget"返回的指针将会遗失,因此可能导致资源泄漏。解决这类问题的办法即是:以独立语句将new操作符创建的对象置入智能指针.上述调用过程分拆为:

          std::trl::shared_ptr<widget>  pw(new  Widget);

          processWidget(pw, priority());    

    五、成对使用newdelete时要采用相同形式:即当你在new表达式中使用[],必须在相应的delete表达式中也使用[],如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值