首页  ·  知识 ·  编程语言
实战智能指针(SmartPointer)
网友  中国IT实验室     编辑:德仔   图片来源:网络
一直以来都听闻智能指针的大名,平常项目中并没有使用过,所以也一直没抽空去研究,今天花了点时间自己实现了一个智能指针。 div&
一直以来都听闻智能指针的大名,平常项目中并没有使用过,所以也一直没抽空去研究,今天花了点时间自己实现了一个智能指针。
  要实现一个智能指针就要先实现一个支持引用计数的类,主要负责增加计数,减少计数,销毁持有的对象指针。大致实现如下:
  class RefObject
  template<class T> class RefObject
  {
  public:
  explicit RefObject(T* p):m_RefCount(1),m_pContainer(p)
  {
  }
  ~RefObject()
  {
  decRef();
  }
  void incRef()
  {
  ++m_RefCount;
  }
  void decRef()
  {
  if(--m_RefCount == 0)
  {
  destroy();
  }
  }
  T* getPtr()
  {
  return m_pContainer;
  }
  private:
  void destroy()
  {
  delete m_pContainer;
  m_pContainer = 0;
  m_RefCount = 0;
  delete this;
  }
  private:
  T*           m_pContainer;
  unsigned int m_RefCount;
  private:
  RefObject(const RefObject&);
  const RefObject& operator= (const RefObject&);
  };
  该模板类将持有一个使用者指定的对象T类型的指针,并维护该指针被引用的数量。在destroy函数中销毁T类型指针和自己。为什么要删除自己呢?这是因为RefObject将和T共存亡。因为我希望用户可以将任意类放入智能指针内部,而不是从RefObject继承下来。所以RefObject是为T而生(由SmartPtr new出来的)。当用户直接使用RefObject时需要自己去调用incRef和decRef去增加和减少计数,因此即将设计的类SmartPtr实际上就是封装下RefObject,不需要用户来调这两个函数。SmartPtr实现大致如下:
  class SmartPtr
  template<class T> class SmartPtr
  {
  public:
  SmartPtr():m_pRefObject(0)
  {
  }
  explicit SmartPtr(T* p)
  {
  m_pRefObject = new RefObject<T>(p);
  }
  SmartPtr(const SmartPtr& other):m_pRefObject(other.m_pRefObject)
  {
  m_pRefObject->incRef();
  }
  ~SmartPtr()
  {
  if(m_pRefObject)
  m_pRefObject->decRef();
  }
  const SmartPtr<T>& operator= (const SmartPtr& other)
  {
  if(m_pRefObject != other.m_pRefObject)
  {
  m_pRefObject = other.m_pRefObject;
  m_pRefObject->incRef();
  }
  return *this;
  }
  T* operator-> () const
  {
  return m_pRefObject->getPtr();
  }
  T& operator* () const
  {
  return *m_pRefObject->getPtr();
  }
  protected:
  RefObject<T>* m_pRefObject;
  };
 SmartPtr类在hold住一个T类指针时就new出一个RefObject与之相对应,重载赋值操作符来实现每赋值一次增加引用计数,析构函数中减少引用计数。并且重载->操作符
  以使SmartPtr使用起来像一个真正的指针。
  一切都非常简单,不是吗?当然这两个类实现的并不完整,有很多细小的功能没有提供,但大体脉络是抓住了。
  下面看看如何使用吧:
  int main()
  {
  {
  SmartPtr<CSample> spSam(new CSample());
  spSame->DoSomething();
  }
  }
  完全不用delete,实现垃圾自动回收效果,很酷吧。 哈哈,我初次写完这些代码是也觉的很爽,终于体验到了使用Java,C#的感觉了。可是智能指针还有一个致命的问题,
  这个问题就像是一盘美食中的苍蝇,让你心头非常不爽,并且对智能指针的好感立刻全无。哈哈哈,那就是:循环引用。当你写的类中不幸出现循环引用,那么你的智能指针将
  不在智能,内存泄露随之产生,违背了智能指针的初衷。
  struct A
  {
  SmartPtr<B> b;
  }
  struct B
  {
  SmartPtr<A> a;
  }
  int main()
  {
  SmartPtr<A> aa(new A());
  SmartPtr<B> bb(new B());
  aa.b = bb;
  bb.a = aa;
  }
  如果你写了以上程序(虽然不常见,因为这种设计是非常糟糕的)你的aa和bb将无法释放!!!导致无法释放的原因是什么?这是因为aa获取了一次bb的所有权,但aa销毁时
  并没有释放这次获得的所有权,当把bb赋给aa的b时,bb的引用计数变为2,当bb销毁时bb所持有的对象引用减为1,而并不是0,所有他并不会释放他所持有的实际指针。其实
  这种情况跟你写如下代码一样:
  class C
  {
  A* a;
  C()
  {
  a = new A();
  }
  }
  类C在构造函数里new了一个A,却并没有在析构函数里delete它!
  我的解决方法是设计一个新类:SmartPtrHolder,此类用来存放一个SmartPtr,但并不增加计数。一种比较时髦的解释叫:弱引用。实现大致如下:
  template<class T> class SmartPtrHoder
  {
  public:
  SmartPtrHoder():m_pRefObject(0)
  {
  }
  SmartPtrHoder(const SmartPtr<T>& sp):m_pRefObject(sp.m_pRefObject)
  {
  }
  SmartPtrHoder& operator= (const SmartPtr<T>& sp)
  {
  if(m_pRefObject != sp.m_pRefObject)
  {
  m_pRefObject = sp.m_pRefObject;
  }
  return *this;
  }
  SmartPtr<T> use()
  {
  return SmartPtr<T>(*this);
  }
  protected:
  RefObject<T>* m_pRefObject;
  };
  这次重载的赋值操作符中并不增加SmartPtr的引用计数。当你需要使用这个SmartPtr时需要调用use()函数来把SmartPtr从SmartPtrHolder中取出来。
  另外需要在SmartPtr中增加一个构造函数以支持从SmartPtrHoder中构造出一个SmartPtr。代码如下:
  explicit SmartPtr(SmartPtrHoder<T>& spHolder):m_pRefObject(spHolder.m_pRefObject)
  {
  m_pRefObject->incRef();
  }
  这样再次设计A和B时:
  struct A
  {
  SmartPtrHolder<B> b;
  }
  struct B
  {
  SmartPtrHolder<A> a;
  }
  这样就不会出现循环引用了。
 
本文作者:网友 来源:中国IT实验室
CIO之家 www.ciozj.com 微信公众号:imciow
   
免责声明:本站转载此文章旨在分享信息,不代表对其内容的完全认同。文章来源已尽可能注明,若涉及版权问题,请及时与我们联系,我们将积极配合处理。同时,我们无法对文章内容的真实性、准确性及完整性进行完全保证,对于因文章内容而产生的任何后果,本账号不承担法律责任。转载仅出于传播目的,读者应自行对内容进行核实与判断。请谨慎参考文章信息,一切责任由读者自行承担。
延伸阅读