[英]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.