智能指针shared_ptr 用引用计数实现看起来不错,但却存在问题。
1、引用计数更新存在着线程安全;
2、循环引用--使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数)
3、定置删除器和空间分配器
比如打开一个文件的关闭,用malloc开辟出来的空间,用引用计数实现会出现问题。
对改变引用计数时加锁可以解决引用计数更新存在着线程安全。
循环引用问题
#include<iostream>
using namespace std;
#include<memory>//库中包含shared_ptr
struct ListNode
{
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void Test2()
{
shared_ptr<ListNode> cur(new ListNode);//1
shared_ptr<ListNode> next(new ListNode);//2
cout << "cur->" << cur.use_count() << endl;//use_count返回shared_ptr的引用计数
cout << "next->" << next.use_count() << endl;
cur->_next = next;//3
next->_prev = cur;//4
cout << "cur->" << cur.use_count() << endl;
cout << "next->" << next.use_count() << endl;
}
//经过语句1、2之后cur和next的引用计数为1,经过语句1、2后引用计数为2。
//但是最后两个对象均不能释放,因为cur的要释放的前提是next释放,而next的释放又依赖于cur的释放,最后就形成了循环引用。
//weak_ptr(弱引用智能指针)会对引用计数会做特殊处理解决这个问题。
struct ListNode
{
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
定置删除器和空间分配器(ps:空间分配器的定置特殊场景下才会这样使用)
如果指针是一个指向文件类型的,在析构函数中只需关闭文件即可,而不是释放空间;如果空间是通过malloc开辟出来的,那么在析构函数中要调用free函数,而不是delete操作符。上述问题通过仿函数就可以解决。具体代码如下:
template<class T>
class Del
{
public:
void operator() (const T *ptr)
{
delete ptr;
}
};
//仿函数实现FClose和Free
struct FClose
{
void operator() (void* ptr)
{
cout << "Close" << endl;
fclose((FILE*)ptr);
}
};
struct Free
{
void operator() (void* ptr)
{
cout << "Free" << endl;
free(ptr);
}
};
template<class T,class Deleter=Del<T>>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new long(1))
{}
SharedPtr(T* ptr,Deleter del)
:_ptr(ptr)
, _pCount(new long(1))
, _del(del)
{}
SharedPtr<T>& operator=(SharedPtr<T> sp)
{
swap(_ptr, sp._ptr);
swap(_pCount, sp._pCount);
return *this;
}
~SharedPtr()
{
cout << "~SharedPtr()" << endl;
Release();
}
T& operator*()
{
return *_ptr;
}
T* GetPtr()//返回原指针_ptr
{
return _ptr;
}
T* operator->()
{
return _ptr;
}
void Release()//释放内存
{
if (--(*_pCount) == 0)
{
_del(_ptr);
delete _pCount;
}
}
private:
T* _ptr;
long* _pCount;
Deleter _del;
};
void Test3()
{
//定制删除器和分配器
SharedPtr<int> sp(new int(6));
SharedPtr<FILE, FClose> sp1(fopen("test.text", "w"), FClose());
SharedPtr<int, Free> sp2((int*)malloc(sizeof(int)* 6), Free());
}
转载于:https://blog.51cto.com/luoyafei/1754965