我正在为智能指针编写代码作为练习。在线使用教程(1,2)我开发了一个带引用计数的普通智能指针类。问题是我无法弄清楚以下内容:
当智能指针检测到a不存在更多引用时 特殊对象,它必须通过指针删除对象 原始类型,即使最终智能的模板参数 指针是基本类型。这是为了避免对象切片 非虚拟析构函数。
我怎样才能做到这一点。基本上我的代码如下所示(来自教程)。
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);
其中Derived
来自Base1
。 Base1具有受保护的构造函数/析构函数。
这是为T
类型的对象存储但我需要通过U类型的对象存储它。我需要保留它。我怎么能这样做?
答案 0 :(得分:6)
您的智能指针需要3个信息块。
首先,指向数据的指针(T*
或其他东西)。
其次,您的引用次数为:std::atomic<int>
或其他。
第三,你的销毁功能(std::function<void(T*)>
或其他)。
首次创建智能指针时,会创建该销毁功能。将智能指针复制到另一个智能指针时,将复制此销毁功能。如果新智能指针的类型与旧指针不匹配,那么该销毁函数将以类型兼容的方式进行封装(std::function<void(Base*)> = std::function<void(Derived*)>
是否可以开箱即用?无论如何,您基本上都是这样做的。)< / p>
默认情况下,此销毁函数仅为delete t
,但作为附带好处,这允许智能指针的用户传递销毁函数,该函数并非总是delete t
。 / p>
有趣的是,相当于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!
}
}
};
或类似的东西
答案 1 :(得分: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类,因此您可以将我在此处显示的功能移动到该类中。使用非模板化基础对该类进行模板化,并在删除引用计数时销毁该指针。少了一件事。