现在的位置: 首页 > 综合 > 正文

条款13:以对象管理资源

2013年03月13日 ⁄ 综合 ⁄ 共 2263字 ⁄ 字号 评论关闭

条款13:以对象管理资源
    (use objects to manage resources.)

内容:
    我们还是以例子来说明问题,假设我们使用一个图片资源的类Image:
    class Image{...};
    接下来我们需要一个工厂函数供应我们特定的Image对象:
    Image* createImage();//返回创建的Image对象的地址。
    这里我们有一个隐性的协议:如果客户使用该工厂函数来获得Image对象,当你使用完了后,你有责任去删除它。
而如果违反了这个协议,那么显然造成了"资源浪费"等一些列问题,客户一般这样用这个对象:
    {
        Image* pcurImage=createImage();
        ...
        delete pcurImage;
    }
    看起来一切都很良好,但是有些情况下就会出问题:如果在"..."代码区段出现return,goto,continue等语句时,我
们往往会忘记delete pcurImage;这句代码;如果执行到释放代码之前有异常抛出,我们就没有机会去释放申请的资源,
...;潜在的危险太多,太可怕了,客户根本就不好去预期哪种情况会出现,现在该怎么办?我们这里推荐的一种解决方
案是:运用对象来管理资源。为什么会想到把资源放到对象里面呢?由于对象超出它的作用域时候会自动调用析构函数,
我们只要在析构函数中调用它的释放资源函数就可以了!想法不错吧?呵呵,而在用法上我们要注意两点:(1)被管理对
象创建的时候要立即被放入对象中,这种观点被称为"资源取得时机便是初始化时机"(Resource Acquisition Is
Initialization;RAII);(2)管理对象(managing object)运用析构函数确保资源被释放。
    标准库中auto_ptr就是对这种情形设置的特制产品,也就是"只能指针",下面我们来示范如何使用它:
    {
        std::auto_ptr<Image> aptrImage(createImage());
        ... //退出作用域的时候自动调用析构函数释放占用的资源
    }
    怎么样?所有问题都解决了吧,但这里我们要注意一下auto_ptr的用法,它的copying 构造和copying assign操作
函数有点奇怪,看下面代码:
    {
        using namespace std;
        auto_ptr<Image > aptrImage1(createImage()); //aptrImage1指向createImage()返回的对象
        auto_ptr<Image > aptrImage2(aptrImage1); //aptrImage2指向对象,aptrImage1指向null
        aptrImage1 = aptrImage2;                 //aptrImage1指向对象,aptrImage2指向null
        ...                                     
    }
    这里的经过copying构造和copying assign操作函数后,原自动对象管理器就失去了指向原先拥有对象的机会,而将
此机会交给了目标自动管理对象,自己指向null对象。也就是说"auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它",从
另外一方面说明auto_ptr也非管理动态分配资源的最佳工具。于是auto_ptr的替代方案出来了,它就是"引用计数型智慧指针"(
reference-couting smart pointer;RCSP).其实就是在目标对象上引用计数机制,当引用计数为0时,说明没有其它对象在
引用它,它就对目标对象进行资源回收,伪代码形如下:
    void xxx::release(){   
        if(--refCount == 0){
            delete this;
        }       
    }
    TR1的tr1::shared_ptr就是个RCSP,你可以这么用它:
    {
        ...
        using std::tr1;
        shared_ptr<Image > sptrImage1(createImage()); //sptrImage1指向createImage()的返回值对象
        shared_ptr<Image > sptrImage2(sptrImage1); //sptrImage2与sptrImage1指向同一个对象
        sptrImage1 = sptrImage2;                   //同上
        ...                                        //sptrImage1与sptrImage2与他们所指向对象被自动销毁。
    }
    你看,这样的复制行为就正常的多了,是不是?呵呵,ok,it's over!
   
    请记住:
    〓 为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
    〓 两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,因为其copying行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null.

抱歉!评论已关闭.