• 智能指针2


    一、指针指针能做什么
    在编写C++程序的时候,让我们最头痛的问题就是内存泄露,也就是说:
    int* pt = new int;
    delete pt;
    必须保证new和delete必须成对出现。作为程序员,我们最理想的指针,是可以像使用普通变量一样来使用的指针,这个指针可以在恰当的时候被自动释放。智能指针就是这样的一个指针,它的任务是要保证每一个被动态分配的内存块都能够被释放。
    就像我们在写handle = CreateSession(...);和CloseSession(handle);的时候,因为要保证这两者成对出现,我们习惯的做法是将其分别放在一个类的构造和析构函数当中。这样,就能保证每一个CreateSession都有对应的CloseSession了。
    同样,为了保证new和delete的成对出现,智能指针也采用同样的做法——分别放在构造和析构函数中去。
    class intptr 

    private: 
     int* m_p; 
    public: 
     intptr(int* p){ m_p = p; } 
     ~intptr(){ delete m_p; } 
     int& operator*(){ return *m_p; } 
    };
    我们可以方便的执行以下代码,而不必担心内存泄漏的问题:
    somefunction()
    {
     intptr pi(new int); 
     *pi = 10; 
     int a = *pi; 
     ………… 
    }
    以上我们给出的“智能指针”有个致命错误。设想我们执行以下代码会有怎样的情况发生:
    void somefunction()
    {
     intptr pt1(new int);
     intptr pt2(new int);
     *pt1 = 10;
     pt2 = pt1;
    }
    对于普通指针来说,pt2 = pt1只是让pt2指向与pt1相同的地址,但是对于我们的智能指针来说,pt2原先指向的地址被泄露掉了,而pt1所指向的地址被释放了两次。所以,我们给每个new出来的内存地址对应的分配一个“被指向计数器”,由它记录这块内存地址被多少指针所指向。


    二、如何改进
    于是我们得到这样一个“智能指针”,它已经可以满足我们对intptr 类型的全部需要了:
    class intptr
    {
    private:
     size_t* m_count;
     int* m_p;
    public:
     intptr(int* p)
     {
      m_p = p;
      m_count = new size_t;   // 建立一个计数器
      *m_count = 1;   // 初始化计数值为
      printf("smart pointer created.\n");
     }
     intptr(const intptr& rhs) // 拷贝构造函数
     {
      m_p = rhs.m_p;   // 指向同一块内存
      m_count = rhs.m_count; // 使用同一个计数器
      (*m_count)++;   // 计数器自增
      printf("new smart pointer added.\n");
     }
     ~intptr()
     {
      (*m_count)--;    // 计数器自减
      if( *m_count == 0 )  // 已经没有别的指针指向该内存块了
      {
       delete m_p; 
       delete m_count;
       printf("smart pointer removed and memory deleted\n");
      }
      else
      {
       printf("smart pointer removed\n");
      }
     }
     intptr& operator= (const intptr& rhs)
     {
      if(m_p == rhs.m_p) return *this; // 是否本来就指向同一内存块,是则返回
      (*m_count)--;   // 原内存块计数器减
      if( *m_count == 0 )  // 是否已经没有别的指针指向原内存块了
      {
       delete m_p; 
       delete m_count; 
       printf("original memory deleted\n");
      }
      m_p = rhs.m_p;    // 指向同一内存块
      m_count = rhs.m_count;  // 使用同一个计数器
      (*m_count)++;    // 计数器加
      printf("new smart pointer added to current memory\n");
      return *this;
     }
     int & operator*()
     {
      return *m_p;
     }
    };

    于是当我们执行以下代码时,我们可以得到这样的结果:
    void somefunction()
    {
     intptr pt1(new int);
     *pt1 = 10;
     intptr pt2 = pt1;
     printf("%d\n", *pt2);
     intptr pt3(new int);
     pt3 = pt1;
     printf("%d\n", *pt3);
    }

    smart pointer created.
    new smart pointer added.
    10
    smart pointer created.
    original memory deleted
    new smart pointer added to current memory
    10
    smart pointer removed
    smart pointer removed
    smart pointer removed and memory deleted

     

    三、一种标准的实现
    namespace smart
    {
     // 引用计数类.
     class smart_count
     {
     public:
      smart_count(int c = 0) : use_count(c) {}
      ~smart_count() {}

      // 增加引用计数, 并返回计数值.
      int addref() { return ++use_count; }
      // 减少引用计数, 并返回计数值.
      int release() { return --use_count; }

     private:
      // 计数变量.
      int use_count;
     };

     // 智能指针.
     template <class T>
     class smart_ptr
     {
     public:
      // 构造指针, 并使引用计数置为1.
      explicit smart_ptr (T* ptr) : p(ptr), u(new smart_count(1))
      {}

      // 构造空指针.
      explicit smart_ptr () : p(NULL), u(NULL)
      {}

      // 智能指针析构.
      ~smart_ptr (void)
      {
       // 如果引用计数等于0, 则删除数据和引用计数, 并置p为NULL.
       // 此处需要注意的是, 共用的u并未置为 NULL, 在其它指针析构
       // 时, p为NULL, 则不会重复delete.
       if (p && u->release() <= 0)
       {
        delete p;
        delete u;
        p = NULL;
       }  
      }

      // 智能指针拷贝构造函数.
      smart_ptr (const smart_ptr<T>& t)
      {
       p = t.p;
       u = t.u;

       if (u) // 必须判断空值.
       {
        u->addref(); // 增加引用计数.
       }
      }

      // 指针赋值.
      void operator= (smart_ptr<T>& t)
      {
       // 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
       if (p && u->release() <= 0)
       {
        delete p;
        delete u;
       }

       // 直接赋值.
       p = t.p;
       u = t.u;

       if (u) // 必须判断空值.
       {
        u->addref(); // 增加引用计数.
       }
      }

      // 重载->操作和*操作符.
      T *operator-> (void) { return p; }
      T& operator *(void) { return *p; }
      // 重载!操作符.
      bool operator! () const { return !p;}

      // 重载指针bool值操作符.
      typedef smart_ptr<T> this_type;
      typedef T * this_type::*unspecified_bool_type;
      operator unspecified_bool_type() const { return !p ? 0: &this_type::p; }
      // 得到原指针.
      T* get() { return p; }
      void reset(T* ptr)
      {
       // 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
       if (p && u->release() <= 0)
       {
        delete p;
        delete u;
       }

       // 赋值, 如果是NULL, 则不创建引用计数.
       p = ptr;
       if (p)
        u = new smart_count(1);
       else
        u = NULL;
      }

      void reset(smart_ptr<T>& t)
      {
       // 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
       if (p && u->release() <= 0)
       {
        delete p;
        delete u;
       }

       // 赋值.
       p = t.p;
       u = t.u;

       if (u) // 必须判断空值.
       {
        u->addref(); // 增加引用计数.
       }
      }

     private:
      T* p;
      smart_count* u;
     };

     // 重载==操作符.
     template<class T, class U> inline bool operator==(smart_ptr<T> & a, smart_ptr<U> & b)
     {
      return a.get() == b.get();
     }

     // 重载!=操作符.
     template<class T, class U> inline bool operator!=(smart_ptr<T> & a, smart_ptr<U> & b)
     {
      return a.get() != b.get();
     }
    }

  • 相关阅读:
    Flask + WSGI + Nginx 环境
    sql字段合并与分组聚合
    杭州优科豪马轮胎有限公司北京经销商
    国家与大洲对应关系json数据
    【C#】编码史记
    【C#】Unicode的流言终结者和编码大揭秘
    【WPF】生成二维码
    【WPF】WriteableBitmap和BitmapImage的相互转换
    TPL之延续任务
    【C#】日期格式转换
  • 原文地址:https://www.cnblogs.com/rosesmall/p/2457252.html
Copyright © 2020-2023  润新知