[Chrome源码阅读] 理解Chrome的smart pointer

Chrome代码中大量运用了智能指针来管理对象的指针,解决对象生命期的问题。这篇文章尝试着理解Chrome中定义的几个智能指针类。

1. scoped_ptr/scoped_array/scopred_array_malloc

以scopred开头的智能指针类定义在/src/base/scoped_ptr.h文件中。它们有着很明确的设计目标,对new/new[]/malloc出来的对象指针进行简单的包装,来管理对象的内存分配和释放。比如scoped_ptr类对new/delete进行了简单的包装,提供类似于std::auto_ptr类似的接口,但是却摒弃std::auto_ptr设计中令人诟病的拷贝复制时被管理的对象转移的问题。简单的禁用拷贝和复制函数就可以达到此效果。所以它们的使用场合就非常明确和简单有限:

//   {
//     scoped_ptr<Foo> foo;          // No pointer managed.
//     foo.reset(new Foo("wee"));    // Now a pointer is managed.
//     foo.reset(new Foo("wee2"));   // Foo("wee") was destroyed.
//     foo.reset(new Foo("wee3"));   // Foo("wee2") was destroyed.
//     foo->Method();                // Foo::Method() called.
//     foo.get()->Method();          // Foo::Method() called.
//     SomeFunc(foo.Release());      // SomeFunc takes owernship, foo no longer
//                                   // manages a pointer.
//     foo.reset(new Foo("wee4"));   // foo manages a pointer again.
//     foo.reset();                  // Foo("wee4") destroyed, foo no longer
//                                   // manages a pointer.
//   }  // foo wasn't managing a pointer, so nothing was destroyed.
同时它们也禁止了两个智能指针之间的比,较,因为它们都不允许一个对象同时被2个智能指针同时管理:

private:
  // 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;
 
 

 
 有一点需要注意的是,类中的很多函数用了编译器的检查机制: 
 

  ~scoped_ptr() {
    enum { type_must_be_complete = sizeof(C) };
    delete ptr_;
  }
所以,编译这些模板类时,必须知道管理对象的定义。也就是说被管理对象的定义须在这些模板类定义之前给出。


2. scoped_refptr/RefCounted<T>/RefCountedThreadSafe<T>

除了上面介绍的简单的智能指针之外,还有一个广泛使用的智能指针是针对智能指针的,类似于boost::shared_ptr类,它就是scoped_refptr。它管理的对象需继承于模板类RefCounted<T>,T就是自己本身。RefCounted<T>模板了提供了AddRef和Release函数(yy一下,为什么不叫IncRef和DecRef,意义不更明确吗?),同时还维护一个INT类型的引用计数。RefCountedThreadSafe<T>是线程安全的RefCounted版本,原因在于引用计数不是采用INT类型,而是一个AtomicRefCount类型(原子的)。

我们重点来关注下scoped_refptr模板类:

template <class T>
class scoped_refptr {
 public:
  scoped_refptr() : ptr_(NULL) {
  }

  scoped_refptr(T* p) : ptr_(p) {
    if (ptr_)
      ptr_->AddRef();
  }

  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
    if (ptr_)
      ptr_->AddRef();
  }

  ~scoped_refptr() {
    if (ptr_)
      ptr_->Release();
  }

  T* get() const { return ptr_; }
  operator T*() const { return ptr_; }
  T* operator->() const { return ptr_; }

  scoped_refptr<T>& operator=(T* p) {
    // AddRef first so that self assignment should work
    if (p)
      p->AddRef();
    if (ptr_ )
      ptr_ ->Release();
    ptr_ = p;
    return *this;
  }

  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
    return *this = r.ptr_;
  }

  void swap(T** pp) {
    T* p = ptr_;
    ptr_ = *pp;
    *pp = p;
  }

  void swap(scoped_refptr<T>& r) {
    swap(&r.ptr_);
  }

 protected:
  T* ptr_;
};

scoped_refptr和boost::shared_ptr之间的区别:

boost::shared_ptr自己管理引用计数,每当它的拷贝构造和赋值运算符被调用时,这个引用计数加1。而scoped_refptr不管理引用计数,由对象自己管理。这样就带来下面不同的用法:

boost::shared_ptr的用例(不正确的):

int* pa = new int(3);
{
    boost::shared_ptr<int> psa(pa);
    ...
    boost::shared_ptr<int> psb(pa);
}
psa/psb被销毁时都会尝试删除pa,结果导致double-delete问题出现。

如果换成scoped_ptr,那么就不会出现上述的问题。


关于智能指针的拥有权策略技术,共有deep copy, reference counting, reference linking和destructive copy。<Modern C++ Design>书籍第7章第5节详细讲到了这几种技术的优缺点,而且Loki库也提供这几种策略技术的实作版本。Chrome里面的智能指针和boost::shared_ptr都是采用reference counting技术。







你可能感兴趣的:([Chrome源码阅读] 理解Chrome的smart pointer)