zoukankan      html  css  js  c++  java
  • [Effective C++ --014]在资源管理类中小心copying行为

    第一节 <背景>
    条款13中讲到“资源取得的时机便是初始化时机”并由此引出“以对象管理资源”的概念。通常情况下使用std中的auto_ptr(智能指针)和tr1::shared_ptr(引数智能指针)作为管理资源的对象。
    事实上,这种管理方法十分有效。但是,auto_ptr和tr1::shared_ptr只能管理基于堆(heap-based)的资源,而非heap-based的资源却往往不适合。
    因此,有的时候你需要建立自己的资源管理类。本文介绍的内容是在你建立自己的资源管理类时应该注意的事项。

    第二节 <正文>
    我们知道在C API中处理Mutex的互斥对象,有lock何unlock两个函数可用:

      void lock(Mutex* pm);            // 锁定pm指向的互斥量
      void unlock(Mutex* pm);          // pm指向的互斥量解锁

    假设我们写了Lock类来管理锁。

    class  Mutex{
    public:
        Mutex():Count(0){}
    public:
        int Count;
    };
    void lock(Mutex* pm){pm->Count++;}
    void unlock(Mutex* pm){pm->Count--;}
    class Lock
    {
    public:
        explicit Lock(Mutex* pm):mutexPtr(pm)
        {lock(mutexPtr);}          // 将mutexPtr指向的互斥变量加锁
        ~Lock(){unlock(mutexPtr);} // 将mutexPtr指向的互斥变量解锁
    private :
        Mutex * mutexPtr;
    };

    上面代码满足RAII(Resource Acquisition is Initialization)原则即,资源在获取时既是初始化时,失去时既是清理时。
    想象下面的场景时,程序的输出结果是什么。

    1     Mutex m;
    2     cout << "Mutex is " << m.Count << endl;
    3     Lock m1(&m);
    4     cout << "Mutex is " << m.Count << endl;
    5     Lock m2(m1);
    6     cout << "Mutex is " << m.Count << endl;
    7     m1.~Lock();
    8     cout << "Mutex is " << m.Count << endl;

    输出结果为:

    Mutex is 0
    Mutex is 1
    Mutex is 1
    Mutex is 0

    这是为什么呢?前两个0和1输出无可厚非,第三个的输出为拿m1作为实例对象去赋值给m2,操作对象为m1,不会直接影响m;第四个互斥量m的管理者m1被销毁了,那么m也就被解锁了。

    在上面的例子中,m的值不断被变更,显然,这种资源的管理的方式是不合理的。

    可能的解决方法:

    1.禁止复制。禁止复制的做法具体的可参照条款6的说明。

    class UnCopyable {
    public:
         UnCopyable(){}
    private:
         UnCopyable(const UnCopyable& ths) {
         }
    };
    class Lock:private UnCopyable {
        ...
    }

    2.使用引用计数智能指针:tr1::shared_ptr。

    从条款13我们已经知道引用计数智能指针会跟踪使用该资源的所有对象数,计数为0时,资源会被删除。注意,这里删除互斥量m不是我们所期待的,我们期待是解锁互斥量

    幸运的是tr1::shared_ptr允许自定义所谓的“删除”动作,该动作是在计数为0时执行的。于是类Lock可以是下面的样子。

    class Lock
    {
    public:
         explicit Lock(Mutex* pm):mutexPtr(pm,unlock)
         {lock(mutexPtr.get());}          // 将mutexPtr指向的互斥变量加锁
    private :
         shared_ptr<Mutex> mutexPtr;
    };

    有没有发觉貌似少了点东西?对,析构函数没有了。因为share_ptr会帮你完成这一工作。

    3.复制管理对象时也复制所管理的资源。

    请回头想一个问题:为什么需要自己的资源管理类?那么,可能的理由是当不需要某个资源时,资源能被正常释放(删除,其他动作)。资源存在多个复件并不可怕,可怕的是复件在该销毁的时候却没有销毁。也就是,管理对象与所管理的资源要一一对应。为了保证这种对应关系,在复制管理对象时也复制所管理的资源。

    4.转移资源的管理权。

    在某些特殊场合下,你可能希望资源只被一个对象拥有,也就是管理对象在copying时要进行资源所有权的转移。从条款13中讲到的auto_ptr可以完美的实现这个需求。

    ■总结
    1.复制管理对象时,请一并复制对象所管理的资源,资源的copy行为决定了管理对象的copy行为
    2.普遍的RAII class的copy行为是抑制复制,使用引用计数

  • 相关阅读:
    Swagger2 添加HTTP head参数
    获取枚举类型描述
    JS设置cookie、读取cookie、删除cookie
    ES6中Promise的入门(结合例子)
    阮一峰的ES6---Promise对象
    model_util.py
    IfcSpatialElementType
    labelme coco
    python opencv KeyPoint
    IfcSpatialZoneType
  • 原文地址:https://www.cnblogs.com/hustcser/p/4103527.html
Copyright © 2011-2022 走看看