IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    scoped_ptr,shared_ptr和weak_ptr用法和实现方法

    admin发表于 2012-07-14 15:30:50
    love 0

    scoped_ptr, shared_ptr和weak_ptr的都称为智能指针,但是各个的用法都不太一样,

    本文就就详细介绍其具体用法以及具体实现方法。

    • 用法介绍

    1. scoped_ptr: “scoped”即作用域的含义,主要用于指针在某个作用域中有效,当离开作用域时,该指针自动释放,这就避免了出现忘记回收内存的情形,同时scoped_ptr的复制构造函数和赋值函数为私有,这样就避免了指针的传递, 示例如下:
    2. scoped g_ptr;
      {
      scoped_ptr ptr(new T);
       g_ptr = ptr;//illegal, because the assign operator is private.
      //T* will deleted when it goes out of this scoped.
      }
      
    3. shared_ptr: 共享所有权的智能指针,当某个对象需要在多个地方使用时,被多个指针引用,此时程序可能就无法确定在何时释放该对象,因此就需要使用“引用计数”的方法,当对象被引用的次数为0时,自动释放该对象,示例代码如下:
    4. shared_ptr g_ptr;
      {
        shared_ptr ptr; // reference count is 1.
        g_ptr = ptr;  //now reference count is 2
        // when it goes out of this scoped, the reference count is 1
      }
      //the reference count is 1.
      g_ptr->foo();
      
    5. weak_ptr: shared_ptr是强引用,而weak_ptr是弱引用;当使用shared_ptr出现循环引用时,造成内存无法释放,如果 你能预料到此种情况发生,你就应该使用weak_ptr类型;另外weak_ptr不会增加所指对象的引用计数,但是会增加计数器的对象引用计数,这点可以后面的实现中看到这点,详细请点击这里
    • 实现原理

    1. scoped_ptr的实现:其主要是在其析构函数中delete对象,同时将copy constructure 和 assign operator设置为私有,避免了指针的传递,这点比auto_ptr安全的多;另外scoped_array是针对new[]和delete[]的只能指针,具体实现请见附件。
    2. shared_ptr的实现:其主要用到了引用计数,每个指向同一对象shared_ptr都指向同一个计数器,其中计数器的定义如下,当shared_ptr对象复制时,对象引用计数加1,当shared_ptr对象释放时,引用计数减1,当引用计数减为0时,delete所指的对象。具体实现请看附件。
    3. 
      // This class is an internal implementation detail for shared_ptr.
      class SharedPtrControlBlock {
       template  friend class shared_ptr;
       template  friend class weak_ptr;
       private:
       SharedPtrControlBlock() : refcount_(1), weak_count_(1) { }
       int refcount_; //对象的引用计数
       int weak_count_; //weak_ptr的引用计数
      };
      
    4. weak_ptr的实现:需要注意的一点weak_ptr需要使用shared_ptr构造,不能直接使用原始指针进行构造,它主要操作的是weak_count_计数器,具体实现请看附件。

    • 代码实现

    scoped_ptr

    // A scoped_ptr is like a T*, except that the destructor of scoped_ptr
    // automatically deletes the pointer it holds (if any).
    // That is, scoped_ptr owns the T object that it points to.
    // Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object.
    // Also like T*, scoped_ptr is thread-compatible, and once you
    // dereference it, you get the threadsafety guarantees of T.
    //
    // The size of a scoped_ptr is small:
    // sizeof(scoped_ptr) == sizeof(C*)
    template 
    class scoped_ptr {
     public:
    
      // The element type
      typedef C element_type;
    
      // Constructor.  Defaults to intializing with NULL.
      // There is no way to create an uninitialized scoped_ptr.
      // The input parameter must be allocated with new.
      explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
    
      // Destructor.  If there is a C object, delete it.
      // We don't need to test ptr_ == NULL because C++ does that for us.
      ~scoped_ptr() {
        enum { type_must_be_complete = sizeof(C) };
        delete ptr_;
      }
    
      // Reset.  Deletes the current owned object, if any.
      // Then takes ownership of a new object, if given.
      // this->reset(this->get()) works.
      void reset(C* p = NULL) {
        if (p != ptr_) {
          enum { type_must_be_complete = sizeof(C) };
          delete ptr_;
          ptr_ = p;
        }
      }
    
      // Accessors to get the owned object.
      // operator* and operator-> will assert() if there is no current object.
      C& operator*() const {
        assert(ptr_ != NULL);
        return *ptr_;
      }
      C* operator->() const  {
        assert(ptr_ != NULL);
        return ptr_;
      }
      C* get() const { return ptr_; }
    
      // Comparison operators.
      // These return whether two scoped_ptr refer to the same object, not just to
      // two different but equal objects.
      bool operator==(C* p) const { return ptr_ == p; }
      bool operator!=(C* p) const { return ptr_ != p; }
    
      // Swap two scoped pointers.
      void swap(scoped_ptr& p2) {
        C* tmp = ptr_;
        ptr_ = p2.ptr_;
        p2.ptr_ = tmp;
      }
    
      // Release a pointer.
      // The return value is the current pointer held by this object.
      // If this object holds a NULL pointer, the return value is NULL.
      // After this operation, this object will hold a NULL pointer,
      // and will not own the object any more.
      C* release() WARN_UNUSED_RESULT {
        C* retVal = ptr_;
        ptr_ = NULL;
        return retVal;
      }
    
     private:
      C* ptr_;
    
      // Forbid comparison of scoped_ptr types.  If C2 != C, it totally doesn't
      // make sense, and if C2 == C, it still doesn't make sense because you should
      // never have the same object owned by two different scoped_ptrs.
      template  bool operator==(scoped_ptr const& p2) const;
      template  bool operator!=(scoped_ptr const& p2) const;
    
      // Disallow evil constructors
      scoped_ptr(const scoped_ptr&);
      void operator=(const scoped_ptr&);
    };
    
    // Free functions
    template 
    void swap(scoped_ptr& p1, scoped_ptr& p2) {
      p1.swap(p2);
    }
    
    template 
    bool operator==(C* p1, const scoped_ptr& p2) {
      return p1 == p2.get();
    }
    
    template 
    bool operator!=(C* p1, const scoped_ptr& p2) {
      return p1 != p2.get();
    }
    
    // scoped_array is like scoped_ptr, except that the caller must allocate
    // with new [] and the destructor deletes objects with delete [].
    //
    // As with scoped_ptr, a scoped_array either points to an object
    // or is NULL.  A scoped_array owns the object that it points to.
    // scoped_array is thread-compatible, and once you index into it,
    // the returned objects have only the threadsafety guarantees of T.
    //
    // Size: sizeof(scoped_array) == sizeof(C*)
    template 
    class scoped_array {
     public:
    
      // The element type
      typedef C element_type;
    
      // Constructor.  Defaults to intializing with NULL.
      // There is no way to create an uninitialized scoped_array.
      // The input parameter must be allocated with new [].
      explicit scoped_array(C* p = NULL) : array_(p) { }
    
      // Destructor.  If there is a C object, delete it.
      // We don't need to test ptr_ == NULL because C++ does that for us.
      ~scoped_array() {
        enum { type_must_be_complete = sizeof(C) };
        delete[] array_;
      }
    
      // Reset.  Deletes the current owned object, if any.
      // Then takes ownership of a new object, if given.
      // this->reset(this->get()) works.
      void reset(C* p = NULL) {
        if (p != array_) {
          enum { type_must_be_complete = sizeof(C) };
          delete[] array_;
          array_ = p;
        }
      }
    
      // Get one element of the current object.
      // Will assert() if there is no current object, or index i is negative.
      C& operator[](std::ptrdiff_t i) const {
        assert(i >= 0);
        assert(array_ != NULL);
        return array_[i];
      }
    
      // Get a pointer to the zeroth element of the current object.
      // If there is no current object, return NULL.
      C* get() const {
        return array_;
      }
    
      // Comparison operators.
      // These return whether two scoped_array refer to the same object, not just to
      // two different but equal objects.
      bool operator==(C* p) const { return array_ == p; }
      bool operator!=(C* p) const { return array_ != p; }
    
      // Swap two scoped arrays.
      void swap(scoped_array& p2) {
        C* tmp = array_;
        array_ = p2.array_;
        p2.array_ = tmp;
      }
    
      // Release an array.
      // The return value is the current pointer held by this object.
      // If this object holds a NULL pointer, the return value is NULL.
      // After this operation, this object will hold a NULL pointer,
      // and will not own the object any more.
      C* release() WARN_UNUSED_RESULT {
        C* retVal = array_;
        array_ = NULL;
        return retVal;
      }
    
     private:
      C* array_;
    
      // Forbid comparison of different scoped_array types.
      template  bool operator==(scoped_array const& p2) const;
      template  bool operator!=(scoped_array const& p2) const;
    
      // Disallow evil constructors
      scoped_array(const scoped_array&);
      void operator=(const scoped_array&);
    };
    
    // Free functions
    template 
    void swap(scoped_array& p1, scoped_array& p2) {
      p1.swap(p2);
    }
    
    template 
    bool operator==(C* p1, const scoped_array& p2) {
      return p1 == p2.get();
    }
    
    template 
    bool operator!=(C* p1, const scoped_array& p2) {
      return p1 != p2.get();
    }
    
    // This class wraps the c library function free() in a class that can be
    // passed as a template argument to scoped_ptr_malloc below.
    class ScopedPtrMallocFree {
     public:
      inline void operator()(void* x) const {
        free(x);
      }
    };
    
    // scoped_ptr_malloc is similar to scoped_ptr, but it accepts a
    // second template argument, the functor used to free the object.
    
    template
    class scoped_ptr_malloc {
     public:
    
      // The element type
      typedef C element_type;
    
      // Constructor.  Defaults to intializing with NULL.
      // There is no way to create an uninitialized scoped_ptr.
      // The input parameter must be allocated with an allocator that matches the
      // Free functor.  For the default Free functor, this is malloc, calloc, or
      // realloc.
      explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
    
      // Destructor.  If there is a C object, call the Free functor.
      ~scoped_ptr_malloc() {
        free_(ptr_);
      }
    
      // Reset.  Calls the Free functor on the current owned object, if any.
      // Then takes ownership of a new object, if given.
      // this->reset(this->get()) works.
      void reset(C* p = NULL) {
        if (ptr_ != p) {
          free_(ptr_);
          ptr_ = p;
        }
      }
    
      // Get the current object.
      // operator* and operator-> will cause an assert() failure if there is
      // no current object.
      C& operator*() const {
        assert(ptr_ != NULL);
        return *ptr_;
      }
    
      C* operator->() const {
        assert(ptr_ != NULL);
        return ptr_;
      }
    
      C* get() const {
        return ptr_;
      }
    
      // Comparison operators.
      // These return whether a scoped_ptr_malloc and a plain pointer refer
      // to the same object, not just to two different but equal objects.
      // For compatibility wwith the boost-derived implementation, these
      // take non-const arguments.
      bool operator==(C* p) const {
        return ptr_ == p;
      }
    
      bool operator!=(C* p) const {
        return ptr_ != p;
      }
    
      // Swap two scoped pointers.
      void swap(scoped_ptr_malloc & b) {
        C* tmp = b.ptr_;
        b.ptr_ = ptr_;
        ptr_ = tmp;
      }
    
      // Release a pointer.
      // The return value is the current pointer held by this object.
      // If this object holds a NULL pointer, the return value is NULL.
      // After this operation, this object will hold a NULL pointer,
      // and will not own the object any more.
      C* release() WARN_UNUSED_RESULT {
        C* tmp = ptr_;
        ptr_ = NULL;
        return tmp;
      }
    
     private:
      C* ptr_;
    
      // no reason to use these: each scoped_ptr_malloc should have its own object
      template 
      bool operator==(scoped_ptr_malloc const& p) const;
      template 
      bool operator!=(scoped_ptr_malloc const& p) const;
    
      static FreeProc const free_;
    
      // Disallow evil constructors
      scoped_ptr_malloc(const scoped_ptr_malloc&);
      void operator=(const scoped_ptr_malloc&);
    };
    
    template
    FP const scoped_ptr_malloc::free_ = FP();
    
    template inline
    void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) {
      a.swap(b);
    }
    
    template inline
    bool operator==(C* p, const scoped_ptr_malloc& b) {
      return p == b.get();
    }
    
    template inline
    bool operator!=(C* p, const scoped_ptr_malloc& b) {
      return p != b.get();
    }
    

    shared_ptr 和weak_ptr

    template  class shared_ptr;
    template  class weak_ptr;
    
    // This class is an internal implementation detail for shared_ptr.
    class SharedPtrControlBlock {
      template  friend class shared_ptr;
      template  friend class weak_ptr;
     private:
      SharedPtrControlBlock() : refcount_(1), weak_count_(1) { }
      int refcount_;
      int weak_count_;
    };
    
    // NOTE: It is strongly encouraged to use scoped_ptr if you could.
    //       shared_ptr should be only used at the cases that
    //       there is no clear owner for the object, anyone reference the object
    //       may need take care if the object should be deleted.
    // NOTE: Don't use this class to replace boost::shared_ptr when working with
    //       thrift.
    template 
    class shared_ptr {
      template  friend class weak_ptr;
     public:
      typedef T element_type;
    
      explicit shared_ptr(T* ptr = NULL)
          : ptr_(ptr),
            control_block_(ptr != NULL ? new SharedPtrControlBlock : NULL) {
      }
    
      // Copy constructor: makes this object a copy of ptr
      template 
      shared_ptr(const shared_ptr& ptr)  // NOLINT
          : ptr_(NULL),
            control_block_(NULL) {
        Initialize(ptr);
      }
      // Need non-templated version to prevent the compiler-generated default
      shared_ptr(const shared_ptr& ptr)  // NOLINT
          : ptr_(NULL),
            control_block_(NULL) {
        Initialize(ptr);
      }
    
      // Assignment operator. Replaces the existing shared_ptr with ptr.
      template 
      shared_ptr& operator=(const shared_ptr& ptr) {
        if (ptr_ != ptr.ptr_) {
          shared_ptr me(ptr);   // will hold our previous state to be destroyed.
          swap(me);
        }
        return *this;
      }
    
      // Need non-templated version to prevent the compiler-generated default
      shared_ptr& operator=(const shared_ptr& ptr) {
        if (ptr_ != ptr.ptr_) {
          shared_ptr me(ptr);   // will hold our previous state to be destroyed.
          swap(me);
        }
        return *this;
      }
    
      ~shared_ptr() {
        if (ptr_ != NULL) {
          if (AtomicDecrement(&control;_block_->refcount_) == 0) {
            delete ptr_;
    
            // weak_count_ is defined as the number of weak_ptrs that observe
            // ptr_, plus 1 if refcount_ is nonzero.
            if (AtomicDecrement(&control;_block_->weak_count_) == 0) {
              delete control_block_;
            }
          }
        }
      }
    
      // Replaces underlying raw pointer with the one passed in.  The reference
      // count is set to one (or zero if the pointer is NULL) for the pointer
      // being passed in and decremented for the one being replaced.
      void reset(T* p = NULL) {
        if (p != ptr_) {
          shared_ptr tmp(p);
          tmp.swap(*this);
        }
      }
    
      // Exchanges the contents of this with the contents of r.  This function
      // supports more efficient swapping since it eliminates the need for a
      // temporary shared_ptr object.
      void swap(shared_ptr& r) {
        std::swap(ptr_, r.ptr_);
        std::swap(control_block_, r.control_block_);
      }
    
      // The following function is useful for gaining access to the underlying
      // pointer when a shared_ptr remains in scope so the reference-count is
      // known to be > 0 (e.g. for parameter passing).
      T* get() const {
        return ptr_;
      }
    
      T& operator*() const {
        return *ptr_;
      }
    
      T* operator->() const {
        return ptr_;
      }
    
      int use_count() const {
        return control_block_ ? control_block_->refcount_ : 1;
      }
    
      bool unique() const {
        return use_count() == 1;
      }
    
     private:
      // If r is non-empty, initialize *this to share ownership with r,
      // increasing the underlying reference count.
      // If r is empty, *this remains empty.
      // Requires: this is empty, namely this->ptr_ == NULL.
      template 
      void Initialize(const shared_ptr& r) {
        if (r.control_block_ != NULL) {
          AtomicIncrement(&r.control;_block_->refcount_);
    
          ptr_ = r.ptr_;
          control_block_ = r.control_block_;
        }
      }
    
      T* ptr_;
      SharedPtrControlBlock* control_block_;
    
      template 
      friend class shared_ptr;
    };
    
    // Matches the interface of std::swap as an aid to generic programming.
    template  void swap(shared_ptr& r, shared_ptr& s) {
      r.swap(s);
    }
    
    // Weak ptrs:
    //   The weak_ptr auxiliary class is used to break ownership cycles. A weak_ptr
    //   points to an object that's owned by a shared_ptr, but the weak_ptr is an
    //   observer, not an owner. When the last shared_ptr that points to the object
    //   disappear, the weak_ptr expires, at which point the expired() member
    //   function will return true.
    //   You can't directly get a raw pointer from weak_ptr, to access a weak_ptr's
    //   pointed-to object, use lock() to obtain a temporary shared_ptr.
    //   See the draft C++ standard (as of October 2007 the latest draft is N2461)
    //   for the detailed specification.
    template 
    class weak_ptr {
      template  friend class weak_ptr;
     public:
      typedef T element_type;
    
      // Create an empty (i.e. already expired) weak_ptr.
      weak_ptr() : ptr_(NULL), control_block_(NULL) { }
    
      // Create a weak_ptr that observes the same object that ptr points
      // to.  Note that there is no race condition here: we know that the
      // control block can't disappear while we're looking at it because
      // it is owned by at least one shared_ptr, ptr.
      template  weak_ptr(const shared_ptr& ptr) {
        CopyFrom(ptr.ptr_, ptr.control_block_);
      }
    
      // Copy a weak_ptr. The object it points to might disappear, but we
      // don't care: we're only working with the control block, and it can't
      // disappear while we're looking at because it's owned by at least one
      // weak_ptr, ptr.
      template  weak_ptr(const weak_ptr& ptr) {
        CopyFrom(ptr.ptr_, ptr.control_block_);
      }
    
      // Need non-templated version to prevent default copy constructor
      weak_ptr(const weak_ptr& ptr) {
        CopyFrom(ptr.ptr_, ptr.control_block_);
      }
    
      // Destroy the weak_ptr. If no shared_ptr owns the control block, and if
      // we are the last weak_ptr to own it, then it can be deleted. Note that
      // weak_count_ is defined as the number of weak_ptrs sharing this control
      // block, plus 1 if there are any shared_ptrs. We therefore know that it's
      // safe to delete the control block when weak_count_ reaches 0, without
      // having to perform any additional tests.
      ~weak_ptr() {
        if (control_block_ != NULL &&
            (AtomicDecrement(&control;_block_->weak_count_) == 0)) {
          delete control_block_;
        }
      }
    
      weak_ptr& operator=(const weak_ptr& ptr) {
        if (&ptr; != this) {
          weak_ptr tmp(ptr);
          tmp.swap(*this);
        }
        return *this;
      }
      template  weak_ptr& operator=(const weak_ptr& ptr) {
        weak_ptr tmp(ptr);
        tmp.swap(*this);
        return *this;
      }
      template  weak_ptr& operator=(const shared_ptr& ptr) {
        weak_ptr tmp(ptr);
        tmp.swap(*this);
        return *this;
      }
    
      void swap(weak_ptr& ptr) {
        swap_weak_ptr(ptr_, ptr.ptr_);
        swap_weak_ptr(control_block_, ptr.control_block_);
      }
    
      void reset() {
        weak_ptr tmp;
        tmp.swap(*this);
      }
    
      // Return the number of shared_ptrs that own the object we are observing.
      // Note that this number can be 0 (if this pointer has expired).
      int use_count() const {
        return control_block_ != NULL ? control_block_->refcount_ : 0;
      }
    
      bool expired() const { return use_count() == 0; }
    
     private:
      void CopyFrom(T* ptr, SharedPtrControlBlock* control_block) {
        ptr_ = ptr;
        control_block_ = control_block;
        if (control_block_ != NULL)
          AtomicIncrement(&control;_block_->weak_count_);
      }
    
     private:
      element_type* ptr_;
      SharedPtrControlBlock* control_block_;
    };
    
    template  void swap_weak_ptr(weak_ptr& r, weak_ptr& s) {
      r.swap(s);
    }
    

    您可能对下面文章也感兴趣:

    • C++插件机制的一种实现方法(对象工厂)
    • 模板成员函数为什么不能是虚函数
    • C++ Web编程
    • C中宏使用小贴士与小技巧[原创]
    • C++中extern “c”深层含义


沪ICP备19023445号-2号
友情链接