2013-04-08 56 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; 
    } 
}; 

編輯:

爲了實現這個目標我必須有一個指向原始類型。

我已經張貼在這裏一個問題: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(); 
} 

現在考慮Sptr<Base1> sp(new Derived);其中DerivedBase1的。 Base1保護了構造函數/析構函數。 正在爲類型爲T的對象存儲但我需要通過類型爲U的對象進行存儲。我需要保留該對象。我怎樣才能做到這一點?

+0

如果某個基類沒有虛擬析構函數,並且有人試圖通過指向該基類的指針刪除派生類,那麼某個人做錯了。 – Chad 2013-04-08 18:52:02

+2

爲了達到這個目的,你必須給'SP'一個模板構造函數'SP :: SP(U * u){...}'並以某種方式存儲原始類型'U'(它必須從' T')能夠稍後調用'U'的析構函數。 – Angew 2013-04-08 18:55:35

+0

C++ 11是否規定一個兼容的智能指針必須這樣做?看起來'std :: unique_ptr'沒有:http://ideone.com/iyanmY – Chad 2013-04-08 18:58:55

回答

6

您的智能指針需要3個信息塊。

首先,指向數據的指針(T*什麼的)。

二,您引用次數:std::atomic<int>什麼的。

三,你的銷燬功能(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 2013-04-08 20:24:46

+0

@footy包含代碼演示技術的代碼段。我還包括開始處理分配給'SP '的非'T *'指針構造,以及非'SP '類型。它並不完整,但我希望你明白這一點。你可能想到的一件好事就是最終使用SFINAE來生成有效的'U'類型,只有那些在重載解析時從'T'下降的類型,但這是一種先進的技術。 – Yakk 2013-04-08 20:49:26

+0

謝謝你救了我的命:P – footy 2013-04-10 09:13:09

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類您可以將我在此處展示的功能移到該類中。使該類以非模板化基礎模板化,並在引用計數被刪除時銷燬指針。還有一件事要傳遞。