2013-04-08 15 views
5

私は練習問題としてスマートポインタのコードを書いています。チュートリアルをオンラインで使用する(12)私はリファレンスカウントで通常のスマートポインタクラスを開発しました。問題は、私は次のことを把握することができませんです:非仮想デストラクタのオブジェクトスライシングを避ける

スマートポインタは、これ以上の言及は 特定のオブジェクトに存在しないこと、それはあっても、 元の型へのポインタを経由してオブジェクトを削除する必要があります検出されたとき最終的なスマート ポインタのテンプレート引数は基本型です。これは、非仮想デストラクタ のオブジェクトのスライスを避けるためです。

どうすれば実現できますか?基本的に私のコードは以下のようになります(チュートリアルから)。

template < typename T > class SP 
{ 
private: 
    T* pData;  // pointer 
    RC* reference; // Reference count 

public: 
    SP() : pData(0), reference(0) 
    { 
     // Create a new reference 
     reference = new RC(); 
     // Increment the reference count 
     reference->AddRef(); 
    } 

    SP(T* pValue) : pData(pValue), reference(0) 
    { 
     // Create a new reference 
     reference = new RC(); 
     // Increment the reference count 
     reference->AddRef(); 
    } 

    SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference) 
    { 
     // Copy constructor 
     // Copy the data and reference pointer 
     // and increment the reference count 
     reference->AddRef(); 
    } 

    ~SP() 
    { 
     // Destructor 
     // Decrement the reference count 
     // if reference become zero delete the data 
     if(reference->Release() == 0) 
     { 
      delete pData; 
      delete reference; 
     } 
    } 

    T& operator*() 
    { 
     return *pData; 
    } 

    T* operator->() 
    { 
     return pData; 
    } 

    SP<T>& operator = (const SP<T>& sp) 
    { 
     // Assignment operator 
     if (this != &sp) // Avoid self assignment 
     { 
      // Decrement the old reference count 
      // if reference become zero delete the old data 
      if(reference->Release() == 0) 
      { 
       delete pData; 
       delete reference; 
      } 

      // Copy the data and reference pointer 
      // and increment the reference count 
      pData = sp.pData; 
      reference = sp.reference; 
      reference->AddRef(); 
     } 
     return *this; 
    } 
}; 

EDIT:

は、私は元の型へのポインタを持っている必要があることを達成するために。 delete via a pointer to Derived, not Base

しかし、今のコメントと回答を見て以来、私は両方が関係していると思う:私はここに質問を掲載している

template <typename T> 
template <typename U> 
Sptr<T>::Sptr(U* u) : obj(u),ref(NULL) { 
    //do something 
    ref = new RC(); 
    ref->AddRef(); 
} 

今すぐDerivedBase1から派生しSptr<Base1> sp(new Derived);を考える:私はコンストラクタを持っています。 Base1はコンストラクタ/デストラクタを保護しています。 タイプTのオブジェクトの格納先ですが、タイプUのオブジェクトに格納する必要があります。これを保持する必要があります。どうやってやるの?

+0

基本クラスに仮想デストラクタがなく、誰かがその基本クラスへのポインタによって派生クラスを削除しようとしている場合、その人はそれを間違って実行しています。 – Chad

+2

これを実現するには、 'SP'にテンプレートコンストラクタ' SP :: SP(U * u){...} 'を与えなければならず、何らかの形で元の型' U'( ' T')を呼び出して、後に 'U'のデストラクタを呼び出すことができます。 – Angew

+0

C++ 11は準拠スマートポインタがこれをしなければならないと述べていますか? 'std :: unique_ptr'は以下のようにはなりません:http://ideone.com/iyanmY – Chad

答えて

6

スマートポインタには3つの情報が必要です。

まず、データへのポインタ(T*または何か)。

第2に、参照カウント:std::atomic<int>または何か。

第3に、あなたの破壊関数(std::function<void(T*)>または何か)。

スマートポインタが最初に作成されると、その破壊関数が作成されます。スマートポインタを別のスマートポインタにコピーすると、この破壊関数がコピーされます。新しいスマートポインタのタイプが古いものと一致しない場合、その破壊関数は型互換性のある方法でラップされます(std::function<void(Base*)> = std::function<void(Derived*)>はそのままで動作しますか?

デフォルトでは、この破壊関数はただdelete tですが、副次的な利点として、スマートポインタのユーザーは破棄関数を渡すことができますが、これは必ずしもdelete tではありません。

好都合には、resetに相当する、破壊関数を置き換えます。だから、実際に破棄関数の署名をstd::function<void()>にすると、TU型のスマートポインタの間を動かすことが容易になります。

template < typename T > class SP 
{ 
private: 
    T* pData;  // pointer 
    RC* reference; // Reference count 
    std::function<void()> destroyData; 
public: 
    template<typename U> 
    SP(U* pValue): 
    pData(pValue), 
    reference(nullptr), 
    // store how to destroy pValue now, for later execution: 
    destroyData([pValue]()->void{ 
     delete pValue; 
    }) 
    { 
    // Create a new reference 
    reference = new RC(); 
    // Increment the reference count 
    reference->AddRef(); 
    } 
    // similar for operator=, and you may have to do something for SP<T> as well: 
    template<typename U> 
    SP(const SP<U>& sp): 
    pData(sp.pData), 
    reference(sp.reference), 
    destroyData(sp.destroyData) 
    { 
    // Copy constructor 
    // Copy the data and reference pointer 
    // and increment the reference count 
    reference->AddRef(); 
    } 
    template<typename U> 
    SP<T>& operator = (const SP<U>& sp) 
    { 
    // blah blah blah, then 
    destroyData = sp.destroyData; 
    } 

    ~SP() 
    { 
    // Destructor 
    // Decrement the reference count 
    // if reference become zero delete the data 
    if(reference->Release() == 0) 
    { 
     delete reference; 
     destroyData(); // here I destroyed it! 
    } 
    } 
}; 

またはそのような何か

+0

どのように私は右の破壊関数へのポインタを格納することができます。私はこのスタイルのテンプレートコーディングを新しくして以来、少し具体的になりますか? – footy

+0

@footyのコードスニペットが含まれています。私はまた、非 'T *'ポインタの処理の開始と 'SP 'に割り当てられた非 'SP 'の型を含みました。それは完全ではありませんが、あなたがそのアイデアを得ることを願っています。あなたは、最終的にSFINAEを使用して有効な 'U'型に、過負荷解決の時点で' T'から降りてくるものだけをタイプすることが考えられますが、これは高度な技術です。 – Yakk

+0

これは私の人生を保存してくれてありがとう:P – footy

0

別のアプローチスマートポインタで今

// non templated base 
class DeleterBase { 
    public: 
     virtual ~DeleterBase() { }; 
}; 

template <typename T> 
class Deleter : public DeleterBase { 
    private: 
     T *ptr; 
    public: 
     Deleter(T *p) // remember the pointer with the correct type here 
      : ptr{p} 
     { } 

     ~Deleter() { 
      delete this->ptr; // invokes correct destructor 
     } 
}; 

削除に別のクラスを委任関係:

template <typename T> 
class SP { 
    private: 
     T *p; 
     DeleterBase *deleter; 
    public: 
     template <typename U> // U is deduced to actual type 
     explicit SP(U *p) 
      : p{p}, 
      deleter{new Deleter<U>(p)} // correct type 
     { } 

     // transfer control in the move constructor/assignment 
     // increase the ref count in copy constructor/assignment 

     // now just delete the deleter in the dtor 
     ~SP() { 
      if (reference_count_is_zero()) { // however you implement this 
       delete this->deleter; 
      } 
     } 
}; 

すでにRCクラスがあるので、ここで示した機能をそのクラスに移行できます。そのクラスをテンプレート化されていないベースでテンプレート化し、参照カウントが削除されたらポインタを破棄します。周りを回ることが1つ少ない。

関連する問題