智能指针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());
}