[C++]智能指针

一、RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:

  1. 不需要显式地释放资源。
  2. 采用这种方式,对象所需的资源在其生命期内始终保持有效。

简单来说,智能指针是RAII思想的一种实现。

二、auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。
auto_ptr的实现原理:转移管理权,使得最后一个拷贝对象管理资源,被拷贝对象都被置空。

基础实现:

	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~auto_ptr()
		{
			delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

 导致问题: 

void autoptr_test()
{
	autoptr::auto_ptr<int> p(new int);
	autoptr::auto_ptr<int> p2 = p;
}

析构两次,程序崩溃。

解决:

	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}

		//转移管理权
		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

新问题:

管理权转移,导致对象悬空。

 可能会访问置为nullptr的_ptr。

因此auto_ptr使用不多。

三、unique_ptr

实现原理:防拷贝

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}

		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;


		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;

	};

四、shared_ptr

原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = T())
			:_ptr(ptr)
		{
			_pcount = new int;
			*_pcount = 1;
		}

		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount)
		{
			(*_pcount)++;	
		}

		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				if (--(*_pcount) == 0)
				{
					delete _ptr;
					_ptr = nullptr;
					delete _pcount;
				}

				_ptr = sp._ptr;
				(*sp._pcount)++;
				_pcount = sp._pcount;
			}

			return *this;
		}

		~shared_ptr()
		{
			if (--(*_pcount) == 0)
			{
				delete _ptr;
				_ptr = nullptr;
				delete _pcount;
			}
 		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pcount;
	};

问题:循环引用

struct ListNode
{
	int _val;
	sharedptr::shared_ptr<ListNode> _prev;
	sharedptr::shared_ptr<ListNode> _next;


	~ListNode()
	{
		cout << "~ListNode" << endl;
	}
};
void sharedptr_test2()
{
	sharedptr::shared_ptr<ListNode> n1 = new ListNode;
	sharedptr::shared_ptr<ListNode> n2 = new ListNode;

	n1->_next = n2;
	n2->_prev = n1;
}

 

当程序语句执行完毕,开始析构时,n2先调用析构函数,则n2的*_pcount变为1,未释放空间。

然后n1再调用析构函数,n1的*_pcount 变为1,未释放空间。

此时如果想要释放n2

=> 需要释放(析构)n1中的_next,使得n2的*_pcount变为0

=>需要释放(析构)n1,从而析构n1中的成员_next

=>需要释放(析构)n2中的_prev,使得n1的*_pcount变为0

=>需要释放(析构)n2,从而析构n2中的成员_prev

=>形成闭环

解决:使用weak_ptr

相当于ListNode中的成员_prev和_next不参与管理,引用计数不会+1

struct ListNode
{
	int _val;
	std::weak_ptr<ListNode> _prev;
	std::weak_ptr<ListNode> _next;

	~ListNode()
	{
		cout << "~ListNode" << endl;
	}
};

void sharedptr_test2()
{
	std::shared_ptr<ListNode> n1(new ListNode);
	std::shared_ptr<ListNode> n2(new ListNode);

	n1->_next = n2;
	n2->_prev = n1;
}

定制删除器

void sharedptr_test3()
{
	std::shared_ptr<set<int>> p1(new set<int>[10], [](set<int>* ptr) {delete[] ptr; });
	auto p2 = p1;
	std::shared_ptr<ListNode> p3(new ListNode[10], [](ListNode* ptr) {delete[] ptr; });

}

 

模拟实现:

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{
			_pcount = new int;
			*_pcount = 1;
		}

		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _del(del)
		{}


		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount)
			,_del(sp._del)
		{
			(*_pcount)++;	
		}

		void release()
		{
			_del(_ptr);
			delete _pcount;
		}

		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				if (--(*_pcount) == 0)
				{
					release();
				}

				_ptr = sp._ptr;
				(*sp._pcount)++;
				_pcount = sp._pcount;
				_del = sp._del;
			}

			return *this;
		}

		~shared_ptr()
		{
			if (--(*_pcount) == 0)
			{
				release();
			}
 		}

		int use_count() const
		{
			return *_pcount;
		}

		T* get() const
		{
			return _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pcount;
		function<void(T*)> _del = [](T* _ptr) { delete _ptr; };
	};

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杯酒问苍天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值