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

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

  • 用法介绍

  1. scoped_ptr: “scoped”即作用域的含义,主要用于指针在某个作用域中有效,当离开作用域时,该指针自动释放,这就避免了出现忘记回收内存的情形,同时scoped_ptr的复制构造函数和赋值函数为私有,这样就避免了指针的传递, 示例如下:
  2. scoped<T> g_ptr;
    {
    scoped_ptr<T> 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<T> g_ptr;
    {
      shared_ptr<T> ptr<new T>; // 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 <typename T> friend class shared_ptr;
     template <typename T> 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<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any).
// That is, scoped_ptr<T> owns the T object that it points to.
// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
// Also like T*, scoped_ptr<T> 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<C>) == sizeof(C*)
template <class C>
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 <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
  template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;

  // Disallow evil constructors
  scoped_ptr(const scoped_ptr&);
  void operator=(const scoped_ptr&);
};

// Free functions
template <class C>
void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
  p1.swap(p2);
}

template <class C>
bool operator==(C* p1, const scoped_ptr<C>& p2) {
  return p1 == p2.get();
}

template <class C>
bool operator!=(C* p1, const scoped_ptr<C>& p2) {
  return p1 != p2.get();
}

// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
// with new [] and the destructor deletes objects with delete [].
//
// As with scoped_ptr<C>, a scoped_array<C> either points to an object
// or is NULL.  A scoped_array<C> owns the object that it points to.
// scoped_array<T> is thread-compatible, and once you index into it,
// the returned objects have only the threadsafety guarantees of T.
//
// Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C>
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 <class C2> bool operator==(scoped_array<C2> const& p2) const;
  template <class C2> bool operator!=(scoped_array<C2> const& p2) const;

  // Disallow evil constructors
  scoped_array(const scoped_array&);
  void operator=(const scoped_array&);
};

// Free functions
template <class C>
void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
  p1.swap(p2);
}

template <class C>
bool operator==(C* p1, const scoped_array<C>& p2) {
  return p1 == p2.get();
}

template <class C>
bool operator!=(C* p1, const scoped_array<C>& 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 C, class FreeProc = ScopedPtrMallocFree>
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 <class C2, class GP>
  bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
  template <class C2, class GP>
  bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;

  static FreeProc const free_;

  // Disallow evil constructors
  scoped_ptr_malloc(const scoped_ptr_malloc&);
  void operator=(const scoped_ptr_malloc&);
};

template<class C, class FP>
FP const scoped_ptr_malloc<C, FP>::free_ = FP();

template<class C, class FP> inline
void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
  a.swap(b);
}

template<class C, class FP> inline
bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
  return p == b.get();
}

template<class C, class FP> inline
bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
  return p != b.get();
}

shared_ptr 和weak_ptr

template <typename T> class shared_ptr;
template <typename T> class weak_ptr;

// This class is an internal implementation detail for shared_ptr.
class SharedPtrControlBlock {
  template <typename T> friend class shared_ptr;
  template <typename T> 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 <typename T>
class shared_ptr {
  template <typename U> 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 <typename U>
  shared_ptr(const shared_ptr<U>& ptr)  // NOLINT
      : ptr_(NULL),
        control_block_(NULL) {
    Initialize(ptr);
  }
  // Need non-templated version to prevent the compiler-generated default
  shared_ptr(const shared_ptr<T>& ptr)  // NOLINT
      : ptr_(NULL),
        control_block_(NULL) {
    Initialize(ptr);
  }

  // Assignment operator. Replaces the existing shared_ptr with ptr.
  template <typename U>
  shared_ptr<T>& operator=(const shared_ptr<U>& ptr) {
    if (ptr_ != ptr.ptr_) {
      shared_ptr<T> 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<T>& operator=(const shared_ptr<T>& ptr) {
    if (ptr_ != ptr.ptr_) {
      shared_ptr<T> 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<T> 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<T>& 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 <typename U>
  void Initialize(const shared_ptr<U>& 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 <typename U>
  friend class shared_ptr;
};

// Matches the interface of std::swap as an aid to generic programming.
template <typename T> void swap(shared_ptr<T>& r, shared_ptr<T>& 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 <typename T>
class weak_ptr {
  template <typename U> 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 <typename U> weak_ptr(const shared_ptr<U>& 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 <typename U> weak_ptr(const weak_ptr<U>& 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 <typename U> weak_ptr& operator=(const weak_ptr<U>& ptr) {
    weak_ptr tmp(ptr);
    tmp.swap(*this);
    return *this;
  }
  template <typename U> weak_ptr& operator=(const shared_ptr<U>& 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 <typename T> void swap_weak_ptr(weak_ptr<T>& r, weak_ptr<T>& s) {
  r.swap(s);
}
来自yaronspace.cn  本文链接:http://yaronspace.cn/blog/archives/1493