C++ Insight: 自动指针 auto_ptr

C/C++ 程序开发里的指针管理——主要时指针释放——很容易出错。

比如下面的例子如果忘记在函数返回之前释放指针,会造成内存泄漏。

void foobar(){
  Car *mycar = new Car();
  ...
  delete mycar;       // Q: The problem
  return;
}

用 auto_ptr 自动释放指针

利用 C++ 里类对象(不是指针)会自动销毁(调用 destructor )可以提出如下方案。

void foobar_autoptr(){
  auto_ptr<Car> mycar_auto = new Car(); // A: a solution
  ...
  // delete mycar;            //-- not needed
                              //-- mycar destructor will handle it
  return;
}

auto_ptr 的实现

根据上面的需求,利用模板,很容易得出如下解决方案。

template<class T> class auto_ptr {
  public:
    T *vp;

    auto_ptr(T *p):vp(p){}
    ~auto_ptr(){
      if( vp!=NULL ) delete vp;
    }

    operator = (T *target) {
      vp = target;      
    }
}

避免重复释放 Double-Free

关于指针的另一个容易出问题的情况是重复释放。实质就是

delete p; // 第一次释放,假设没问题。
delete p; // 这里会出问题

导致这种情况发生的情形包括函数参数传递

void foobar_autoptr(){
  auto_ptr<Car> mycar_auto = new Car(); 

  bar(mycar);  // 参数传递,
  // 此时指针已经在 bar() 调用结束前被释放

  // 当前函数返回前,指针会被再次释放,就要出问题了
  return;
}

转移控制权

解决办法之一是永远保持所有权的唯一性。因此需要重载 Copy Constructor 和 复制运算符。

template<class T> class auto_ptr {
    auto_ptr(auto_ptr &from){
      vp = from.vp;
      from.vp = NULL;
    }

    operator = (auto_ptr &from) {
      vp = from.vp;
      from.vp = NULL;
    }
}

接口

template<class T> class auto_ptr {
  T& operator *(){
    return *vp;
  }

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

  T* release() {
    T* result = vp;
    vp = NULL;
    return result;
  }

  void reset(T* p = 0) {
    if(vp!=NULL) delete vp;
    vp = p;
  }
}