2

尽管存在大量智能指针问题,但我似乎又遇到了一个问题。我正在尝试实现一个引用计数的智能指针,但是当我在以下情况下尝试它时,引用计数是错误的。这些评论是我认为应该是正确的参考计数。

Sptr<B> bp1(new B);       // obj1: ref count = 1
Sptr<B> bp2 = bp1;        // obj1: ref count = 2
bp2 = new B;              // obj1: ref count = 1, obj2: rec count = 1 **problem**

在我的实现中,我的 obj2 引用计数为 2,因为这段代码:

protected:
    void retain() { 
        ++(*_rc);
        std::cout << "retained, rc: " << *_rc << std::endl;
    }
    void release() {
        --(*_rc);
        std::cout << "released, rc: " << *_rc << std::endl;
        if (*_rc == 0) {
            std::cout << "rc = 0, deleting obj" << std::endl;
            delete _ptr;
            _ptr = 0;
            delete _rc;
            _rc = 0;
        }
    }

private:
    T *_ptr;
    int *_rc;

    // Delegate private copy constructor
    template <typename U>
    Sptr(const Sptr<U> *p) : _ptr(p->get()), _rc(p->rc()) {
        if (p->get() != 0) retain();
    }

    // Delegate private assignment operator
    template <typename U>
    Sptr<T> &operator=(const Sptr<U> *p) {
        if (_ptr != 0) release();
        _ptr = p->get();
        _rc = p->rc();
        if (_ptr != 0) retain();
        return *this;
    }

public:
    Sptr() : _ptr(0) {}
    template <typename U>
    Sptr(U *p) : _ptr(p) { _rc = new int(1); }

    // Normal and template copy constructors both delegate to private
    Sptr(const Sptr &o) : Sptr(&o) {
        std::cout << "non-templated copy ctor" << std::endl;
    }
    template <typename U>
    Sptr(const Sptr<U> &o) : Sptr(&o) {
        std::cout << "templated copy ctor" << std::endl;
    }


    // Normal and template assignment operator
    Sptr &operator=(const Sptr &o) {
        std::cout << "non-templated assignment operator" << std::endl;
        return operator=(&o);
    }
    template <typename U>
    Sptr<T> &operator=(const Sptr<U> &o) {
        std::cout << "templated assignment operator" << std::endl;          
        return operator=(&o);
    }
    // Assignment operator for assigning to void or 0
    void operator=(int) {
        if (_ptr != 0) release();
        _ptr = 0;
        _rc = 0;
    }

构造函数使用 ref count = 1 进行初始化,但在我的赋值运算符中,我保留了对象,使 ref count = 2。但在这种情况下它应该只是 1,因为 bp2 = new B 只是一个指向那个物体。我查看了各种智能指针实现示例,但我似乎无法弄清楚它们如何处理我遇到的这种情况。

谢谢你的时间!

4

3 回答 3

4

无论如何,赋值运算符充满了小错误和不必要的复杂性。例如,当您分配Sptr<T>给它自己时,它会产生有趣的效果。大多数手动编写的赋值运算符应如下所示:

T& T::operator= (T const& other) {
    T(other).swap(*this);
    return *this;
}

... 或者

T& T::operator= (T other) {
    other.swap(*this);
    return *this;
}

一旦有状态的分配器进入游戏,事情就会发生一些变化,但我们可以在这里忽略这个细节。主要思想是利用为复制构造函数和析构函数所做的现有工作。请注意,如果右侧不是 ,则此方法也有效T,即您仍然可以利用相应的构造函数、析构函数和swap()。无论如何,唯一潜在的额外工作是可取的,并且实现起来微不足道:swap()成员。在您的情况下,这也很简单:

template <typename T>
void Sptr<T>::swap(Sptr<T>& other) {
    std::swap(this->_ptr, other._ptr);
    std::swap(this->_rc,  other._rc);
}

只需注意您的赋值运算符采用int: 这是一个非常糟糕的主意!要重置指针,您最好有一个reset()方法。不过,在 C++ 2011 中,您可以合理地使用一种方法来std::nullptr_t实现此目的。

于 2012-11-17T21:54:38.667 回答
2

为原始指针类型定义赋值运算符。否则,它将构造一个引用计数为 1 的新智能指针,然后将其增加到 2。

例子:

template <typename U>
Sptr<T> &operator=(U *p) 
{
    if (_ptr != 0)
    {
      _ptr = p;
      _rc = new int(1);
    }
    return *this;
}

这东西是出了名的棘手,而且可能还有更多的错误在等待。我会让构造函数显式以防止其他无意的构造。

于 2012-11-17T21:47:40.260 回答
1

根据我对您的代码的了解,您需要在智能指针类中使用适当的析构函数来调用release和修复计数器。您至少需要进行修改才能使计数器正常工作。

我没有Sptr(T *)在您的代码中看到构造函数,您是否忽略了它?

作为旁注,我可能会使用 astd::pair来存储计数器和T指针,但您的数据布局可以正常工作。正如另一个答案指出的那样,您应该注意自我分配案例。

这是遵循您的数据布局和接口选择的最小实现:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class Sptr {

protected:
  T *_ptr;
  int *_rc;

  virtual void retain() {
    if (_rc)   // pointing to something
      ++(*_rc);
    clog << "retain : " << *_rc << endl;
  }
  virtual void release() {
    if (_rc) {
      --(*_rc);
      clog << "release : " << *_rc << endl;
      if (*_rc == 0) {
        delete _ptr;
        _ptr = NULL;
        delete _rc;
        _rc = NULL;
      }
    }
  }

public:
  Sptr() : _ptr(NULL), _rc(NULL) {} // no reference

  virtual ~Sptr() { release(); }  // drop the reference held by this

  Sptr(T *p): _ptr(p) {  // new independent pointer
     _rc = new int(0); 
     retain();
  }

  virtual Sptr<T> &operator=(T *p) {
    release();
    _ptr = p;
    _rc = new int(0);
    retain();
    return *this;
  }

  Sptr(Sptr<T> &o) : _ptr(o._ptr), _rc(o._rc) {
    retain();
  }

  virtual Sptr<T> &operator=(Sptr<T> &o) {
    if (_rc != o._rc){  // different shared pointer
      release();
      _ptr = o._ptr;
      _rc = o._rc;
      retain();
    }
    return *this;
  }
};

int main(){
  int *i = new int(5);

  Sptr<int> sptr1(i);
  Sptr<int> sptr2(i);
  Sptr<int> sptr3;

  sptr1 = sptr1;
  sptr2 = sptr1;
  sptr3 = sptr1;

}

_rc是您的类中指针是否与另一个实例共享的指示器。

例如,在这段代码中:

 B *b = new B;
 Sptr<B> sptr1(b);
 Sptr<B> sptr2(b);

sptr1并且sptr2不共享指针,因为分配是单独完成的,因此_rc应该不同,这在我的小型实现中实际上就是这种情况。

于 2012-11-17T22:10:54.973 回答