智能指针的模拟实现shared_ptr 循环引用 定置删除器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针的模拟实现shared_ptr 循环引用 定置删除器相关的知识,希望对你有一定的参考价值。
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 循环引用 定置删除器的主要内容,如果未能解决你的问题,请参考以下文章