shared_ptr智能指针缺陷的解决方法

在上一篇博客【C++:智能指针】中详细介绍了C++中的一些智能指针,其中,shared_ptr智能指针有三个缺陷:

  1. 线程不安全;
  2. 管理资源能力单一:不能管理malloc出来的资源,不能管理文件指针
  3. 可能会产生循环引用的问题

第三个缺陷(循环引用)的解决方法在【C++:智能指针】这篇博客中已经解决了;本篇博客主要介绍第一和第二中缺陷的解决方法;

目录:
      1. 缺陷一:线程不安全
      2. 缺陷二:管理资源能力单一


有缺陷的代码:
template<class T>
class SharedPtr
{
private:
	T* _ptr;
	int* _count;
private:
	void Release()
	{
		if (_ptr && ((*_count--) == 0))
		{
			delete _ptr;
			delete _count;
		}
	}
	void AddCount(SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		_count = sp._count;
		if (_ptr)
			(*_count)++;
	}
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(nullptr)
	{
		if (_ptr)
			_count = new int(1);
	}
	SharedPtr(SharedPtr<T>& sp)
	{
		AddCount(sp);
	}
	SharedPtr<T>& operator=(SharedPtr<T>& sp)
	{
		if (this != &sp)
		//优化:if(_ptr != sp._ptr)
		{
			Release();
			AddCount(sp);
		}
		return *this;
	}
	~SharedPtr()
	{
		Release();
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int Use_count()
	{
		return *_count;
	}
};

缺陷一:线程不安全

shared_ptr的线程安全分为两个方面:

  1. shared_ptr智能指针对象中的引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时进行++或者 - -操作,在为加锁的情况下,会导致计数混乱,这样有可能造成资源泄漏或者程序奔溃的问题,而且++和- -操作本身也不是原子的。
  2. 智能指针管理的对象保存在堆上,两个线程同时去访问,也会导致线程安全问题。

所以为了 保证线程安全,需要在合适的地方加上锁在访问临界资源的地方(临界区)加上锁;访问完后解锁

下面这段代码是,线程安全版本的代码:

template<class T>
class SharedPtr
{
private:
	T* _ptr;
	int* _count;
	mutex m_mtx;
private:
	void Release()
	{
		m_mtx.lock();
		if (_ptr && ((*_count--) == 0))
		{
			delete _ptr;
			delete _count;
		}
		m_mtx.unlock();
	}
	void AddCount(SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		_count = sp._count;
		m_mtx.lock();
		if (_ptr)
			(*_count)++;
		m_mtx.unlock();
	}
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(nullptr)
	{
		m_mtx.lock();
		if (_ptr)
			_count = new int(1);
		m_mtx.unlock();
	}
	SharedPtr(SharedPtr<T>& sp)
	{
		AddCount(sp);
	}
	SharedPtr<T>& operator=(SharedPtr<T>& sp)
	{
		if (this != &sp)
		//优化:if(_ptr != sp._ptr)
		{
			Release();
			AddCount(sp);
		}
		return *this;
	}
	~SharedPtr()
	{
		Release();
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int Use_count()
	{
		return *_count;
	}
};

因为这里使用到了锁,所以也要预防死锁问题的产生。

缺陷二:无法处理非new申请的对象

在上一篇博客中【C++:智能指针】shared_ptr只能处理用new申请出来的对象资源,那么当使用者用shared_ptr管理不是new申请出来的对象时,程序运行到释放资源的这一步就会出现错误。

为了解决这个问题C++又为shared_ptr设计了特殊的删除器;

这个特殊的删除器本质上就是仿函数,利用仿函数的特点来将shared_ptr智能指针管理资源能力单一的缺陷,即将释放管理资源的任务交给定制出来的删除器来完成。

下面是添加删除器之后的代码:

//-------------------------定制删除器---------------------------------
		template<class T>//-------用 new申请出来的
		class DFDel
		{
			public:
				void operator()(T*& ptr)
				{
					delete ptr;
					ptr = nullptr;
				}
		}
		template<class T>//-------用 malloc申请出来的
		class Free
		{
			public:
				void operator()(T*& ptr)
				{
					free(ptr);
					ptr = nullptr;
				}
		}
		template<class T>//-------------------------用 FIFE申请出来的
		class Close
		{
			public:
				void operator()(FIFE*& ptr)
				{
					fclose(ptr);
					ptr = nullptr;
				}
		}
//--------------------------------------------------------
//添加仿函数之后的模板
template<class T,class DF = DFDel<T>>
class SharedPtr
{
private:
	T* _ptr;
	int* _count;
private:
	void Release()
	{
		if (_ptr && ((*_count--) == 0))
		{
			//仿函数:
			DF()(_ptr);
			//DF:  这是一种类型;DF():这是一个匿名对象
		}
	}
	void AddCount(SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		_count = sp._count;
		if (_ptr)
			(*_count)++;
	}
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(nullptr)
	{
		if (_ptr)
			_count = new int(1);
	}
	SharedPtr(SharedPtr<T>& sp)
	{
		AddCount(sp);
	}
	SharedPtr<T>& operator=(SharedPtr<T>& sp)
	{
		if (this != &sp)
		//优化:if(_ptr != sp._ptr)
		{
			Release();
			AddCount(sp);
		}
		return *this;
	}
	~SharedPtr()
	{
		Release();
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int Use_count()
	{
		return *_count;
	}
};

你可能感兴趣的:(C++)