从图可以看出,错误的产生来自于ptr1的”无知“:它并不知道还有其他指针共享着它指向的对象。如果有个办法让ptr1知道,除了它自己外还有两个指针指向基础对象,而它不应该删除基础对象,那么悬垂指针的问题得以解决了。如下图:

  那么何时才可以删除基础对象呢?当然是只有一个指针指向基础对象的时候,这时通过该指针可以大大方方地把基础对象删除了。
  什么是引用计数
  如何来让指针知道还有其他指针的存在呢?这个时候我们该引入引用计数的概念了。引用计数是这样一个技巧,它允许有多个相同值的对象共享这个值的实现。引用计数的使用常有两个目的:
  简化跟踪堆中(也即C++中new出来的)的对象的过程。一旦一个对象通过调用new被分配出来,记录谁拥有这个对象是很重要的,因为其所有者要负责对它进行delete。但是对象所有者可以有多个,且所有权能够被传递,这使得内存跟踪变得困难。引用计数可以跟踪对象所有权,并能够自动销毁对象。可以说引用计数三个简单的垃圾回收体系。这也是本文的讨论重点。
  节省内存,提高程序运行效率。如何很多对象有相同的值,为这多个相同的值存储多个副本是很浪费空间的,所以好做法是让左右对象都共享同一个值的实现。C++标准库中string类采取一种称为”写时复制“的技术,使得只有当字符串被修改的时候才创建各自的拷贝,否则可能(标准库允许使用但没强制要求)采用引用计数技术来管理共享对象的多个对象。这不是本文的讨论范围。
  智能指针实现
  了解了引用计数,我们可以使用它来写我们的智能指针类了。智能指针的实现策略有两种:辅助类与句柄类。这里介绍辅助类的实现方法。
  基础对象类
  首先,我们来定义一个基础对象类Point类,为了方便后面我们验证智能指针是否有效,我们为Point类创建如下接口:
  class Point
  {
  private:
  int x, y;
  public:
  Point(int xVal = 0, int yVal = 0) :x(xVal), y(yVal) { }
  int getX() const { return x; }
  int getY() const { return y; }
  void setX(int xVal) { x = xVal; }
  void setY(int yVal) { y = yVal; }
  };
  辅助类
  在创建智能指针类之前,我们先创建一个辅助类。这个类的所有成员皆为私有类型,因为它不被普通用户所使用。为了只为智能指针使用,还需要把智能指针类声明为辅助类的友元。这个辅助类含有两个数据成员:计数count与基础对象指针。也即辅助类用以封装使用计数与基础对象指针。
  class U_Ptr
  {
  private:
  friend class SmartPtr;
  U_Ptr(Point *ptr) :p(ptr), count(1) { }
  ~U_Ptr() { delete p; }
  int count;
  Point *p;
  };
  为基础对象类实现智能指针类
  引用计数是实现智能指针的一种通用方法。智能指针将一个计数器与类指向的对象相关联,引用计数跟踪共有多少个类对象共享同一指针。它的具体做法如下:
  当创建类的新对象时,初始化指针,并将引用计数设置为1
  当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针相应的引用计数(加1)
  使用赋值操作符对一个对象进行赋值时,处理复杂一点:先使左操作数的指针的引用计数减1(为何减1:因为指针已经指向别的地方),如果减1后引用计数为0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象)。
  析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete对象。
  做好前面的准备后,我们可以来为基础对象类Point书写一个智能指针类了。根据引用计数实现关键点,我们可以写出我们的智能指针类如下:
{
public:
SmartPtr(Point *ptr) :rp(new RefPtr(ptr)) { }
SmartPtr(const SmartPtr &sp) :rp(sp.rp) { ++rp->count; }
SmartPtr& operator=(const SmartPtr& rhs) {
++rhs.rp->count;
if (--rp->count == 0)
delete rp;
rp = rhs.rp;
return *this;
}
~SmartPtr() {
if (--rp->count == 0)
delete rp;
else
cout << "还有" << rp->count << "个指针指向基础对象" << endl;
}
private:
RefPtr *rp;
};