2

そこにスマートポインタの質問の海にもかかわらず、私はもう1つで立ち往生しているようです。参照カウントのスマートポインタを実装しようとしていますが、次の場合に試してみると、参照カウントが間違っています。コメントは、正しい参照カウントである必要があると私が思うものです。

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;
    }

コンストラクターはrefcount= 1で初期化されますが、代入演算子では、オブジェクトを保持し、ref count = 2にします。ただし、この場合は1のみである必要があります。これは、bp2 =newBがへの1つのポインターにすぎないためです。そのオブジェクト。私はさまざまなスマートポインターの実装例を見てきましたが、私が問題を抱えているこのケースをそれらがどのように処理するのか理解できないようです。

御時間ありがとうございます!

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 *)たが、省略しましたか?

std::pairちなみに、カウンターとTポインターを格納するためにおそらくaを使用しますが、データレイアウトはそのまま機能します。別の回答が指摘しているように、自己割り当ての場合にも注意を払う必要があります。

データレイアウトとインターフェイスの選択に従った最小限の実装は次のとおりです。

#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);

sptr1sptr2割り当ては別々に行われたため、ポインタを共有していません。したがって、は_rc異なるはずです。これは、私の小さな実装では事実上当てはまります。

于 2012-11-17T22:10:54.973 に答える