簡體   English   中英

避免對非虛擬析構函數進行對象切片

[英]Avoid object slicing for non-virtual destructors

我正在為智能指針編寫代碼作為練習。 使用在線教程( 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;
    }
};

編輯:

要實現這一點,我必須有一個指向原始類型的指針。

我在這里發了一個問題: 通過指向Derived的指針刪除,而不是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); Derived派生自Base1 Base1具有受保護的構造函數/析構函數。 這是為T類型的對象存儲但我需要通過U類型的對象存儲它。我需要保留它。 我怎樣才能做到這一點?

您的智能指針需要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!
    }
  }
};

或類似的東西

另一種方法是將刪除委托給另一個類

// 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類 ,因此可以將我在此處顯示的功能移動到該類中。 使用非模板化基礎對該類進行模板化,並在刪除引用計數時銷毀該指針。 少走過一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM