[英]Avoid object slicing for non-virtual destructors
我正在為智能指針編寫代碼作為練習。 使用在線教程( 1 , 2 )我已經與引用計數正常的智能指針類。 問題是我無法弄清楚以下內容:
當智能指針檢測到特定對象不再存在引用時,它必須通過指向原始類型的指針刪除該對象,即使最終智能指針的模板參數是基類型也是如此。 這是為了避免非虛擬析構函數的對象切片。
我怎樣才能做到這一點。 基本上我的代碼如下所示(來自教程)。
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()>
,這使得在T
和U
類型智能指針之間移動它更容易。
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.