您的位置:首页 > 其它

智能指针的模拟实现shared_ptr 循环引用 定置删除器

2016-03-23 21:30 330 查看
auto_ptr与scoped_ptr的实现见本人的上篇博客。
三、shared_ptr
shared_ptr的实现原理是通过引用计数来实现,只有当引用计数为1时才释放空间,否则只需将引用计数减1.拷贝和赋值将引用计数加1,具体代码如下:
template <typename T>
class SharedPtr
{
public:
SharedPtr();
SharedPtr(T* ptr);
SharedPtr(const SharedPtr<T>& ap);
~SharedPtr();
//SharedPtr<T>& operator=(const SharedPtr<T>& ptr);//传统写法
SharedPtr<T>& operator=(SharedPtr<T> ap);//现代写法
T& operator*()const;
T* operator->()const;
long GetCount()const;
T* GetPtr()const;
protected:
void _Realease();
protected:
T* _ptr;
long* _pCount;
};
template <typename T>
SharedPtr<T>::SharedPtr() :_ptr(NULL), _pCount(new long(1))
{}
template <typename T>
SharedPtr<T>::SharedPtr(T* ptr) : _ptr(ptr), _pCount(new long(1))
{}
template <typename T>
SharedPtr<T>::SharedPtr(const SharedPtr<T>& ap) : _ptr(ap._ptr), _pCount(ap._pCount)
{
++(*this->_pCount);
}
template <typename T>
SharedPtr<T>::~SharedPtr()
{
this->_Realease();
}
//template <typename T>//传统写法
//SharedPtr<T>& SharedPtr<T>::operator=(const SharedPtr<T>& ap)
//{
//	if (this->_ptr != ap._ptr)
//	{
//		this->_Realease();
//		this->_ptr = ap._ptr;
//		this->_pCount = ap._pCount;
//		++(*this->_pCount);
//	}
//	return *this;
//}
template <typename T>//现代写法
SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<T> ap)
{
swap(this->_ptr, ap._ptr);
swap(this->_pCount, ap._pCount);
return *this;
}
template <typename T>
T& SharedPtr<T>::operator*()const
{
return *(this->_ptr);
}
template <typename T>
T* SharedPtr<T>::operator->()const
{
return this->_ptr;
}
template <typename T>
long SharedPtr<T>::GetCount()const
{
return *(this->_pCount);
}
template <typename T>
T* SharedPtr<T>::GetPtr()const
{
return this->_ptr;
}
template <typename T>
void SharedPtr<T>::_Realease()
{
if (--(*this->_pCount) == 0)
{
delete this->_ptr;
delete this->_pCount;
}
}
然而上面用引用计数实现的简化版看起来不错,但却存在以下问题:

1、引用计数更新存在着线程安全
2、循环引用
3、定置删除器,比如要关闭一个文件,用malloc开辟出来的空间,上述代码均会出现问题
问题1的解决需要对改变引用计数时加锁。我们暂时不讨论,一下我们主要看第二个和第三个问题。
循环引用:

比如有以下结构体和主函数:
struct ListNode
{
shared_ptr<ListNode> _prev; //shared_ptr为库文件中实现的,只需包memory即可使用
shared_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
shared_ptr<ListNode> prev; //语句1
shared_ptr<ListNode> next; //语句2
prev->_next = next;        //语句3
next->_prev = prev;        //语句4
}
经语句1、2之后prev的引用计数为1,经3、4后为2,但是最后两个对象均不能释放,因为prev的要释放的前提是next释放,而next的释放又依赖于prev的释放。最后就形成了循环引用,谁都是放不了。解决方案如下:

struct ListNode
{
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
因为weak_ptr(弱引用智能指针)会对引用计数会做特殊处理(上述情况不加1)。
定置删除器和空间分配器(ps:空间分配器的定置特殊场景下才会这样使用)
eg:如果指针是一个指向文件类型的,在析构函数中只需关闭文件即可,而不是释放空间;如果空间是通过,malloc出来的,那么在析构函数中要调用free函数,而不是delete操作符。上述问题通过仿函数就可以解决。具体代码如下:
template <typename T,class Deleter = Del<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr);
SharedPtr(T* ptr, Deleter del);
SharedPtr(const SharedPtr<T, Deleter>& ap);
~SharedPtr();
//SharedPtr<T, Deleter>& operator=(const SharedPtr<T, Deleter>& ptr);//传统写法
//现代写法
SharedPtr<T, Deleter>& operator=(SharedPtr<T, Deleter> ap);
T& operator*()const;
T* operator->()const;
long GetCount()const;
T* GetPtr()const;
protected:
void _Realease();
protected:
T* _ptr;
long* _pCount;
Deleter _del;
};

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr) :_ptr(ptr), _pCount(new long(1))
{}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr, Deleter del) : _ptr(ptr), _pCount(new long(1)), _del(del)
{}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(const SharedPtr<T, Deleter>& ap) : _ptr(ap._ptr), _pCount(ap._pCount), _del(ap._del)
{
++(*this->_pCount);
}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::~SharedPtr()
{
this->_Realease();
}

//template <typename T, class Deleter = Del<T>>//传统写法
//SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
//{
//	if (this->_ptr != ap._ptr)
//	{
//		this->_Realease();
//		this->_ptr = ap._ptr;
//		this->_pCount = ap._pCount;
//		++(*this->_pCount);
//	}
//	return *this;
//}

template <typename T, class Deleter = Del<T>>//现代写法
SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
{
swap(this->_ptr, ap._ptr);
swap(this->_pCount, ap._pCount);
swap(this->_del, ap._del);
return *this;
}

template <typename T, class Deleter = Del<T>>
T& SharedPtr<T, Deleter>::operator*()const
{
return *(this->_ptr);
}

template <typename T, class Deleter = Del<T>>
T* SharedPtr<T, Deleter>::operator->()const
{
return this->_ptr;
}

template <typename T, class Deleter = Del<T>>
long SharedPtr<T, Deleter>::GetCount()const
{
return *(this->_pCount);
}

template <typename T, class Deleter = Del<T>>
T* SharedPtr<T, Deleter>::GetPtr()const
{
return this->_ptr;
}

template <typename T, class Deleter = Del<T>>
void SharedPtr<T, Deleter>::_Realease()
{
if (--(*this->_pCount) == 0)
{
_del(_ptr);
delete this->_pCount;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shared ptr 智能指针