以下测试代码gitee仓库地址:https://gitee.com/tgwTTT/c-lreant/tree/master/Smart%20pointer

在C++使用内存的时候很容易出现野指针、空指针、内存泄露。所以C++11引入了智能指针来管理内存。在c++11中一共引入了3种指针,即unique_ptr 、shared_ptr、weak_ptr.

一.unique_ptr:他的特点的不⽀持拷⻉,只⽀持移动。如果不需要拷⻉的场景就⾮常建议使⽤他。

struct Date
{
	int _year;
	int _month;
	int _day;
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
	~Date()
	{
		cout << "~Date()" << endl;
	}
};
unique_ptr up1(new Date);
// 不⽀持拷⻉
//unique_ptr up2(up1);
// ⽀持移动,但是移动后up1也悬空,所以使⽤移动要谨慎
unique_ptr up3(move(up1));

shared_unique:他的特点是⽀持拷⻉,也⽀持移动。如果需要拷⻉的场景就需要使⽤他了。底层是⽤引⽤计数的⽅式实现的。

weak_ptr:不能⽤它直接管理资源,weak_ptr的产⽣本质是要解决shared_ptr的⼀个循环引⽤导致内存泄漏的问题。具体细节下⾯我们再细讲

下面我们来了解一下shared_unique的底层实现:

namespace bit {
	template
	class shared_ptr
	{
		public :
		explicit shared_ptr(T* ptr)
			: _ptr(ptr)
			, _pcount(new int(1))
		{}
		shared_ptr(const shared_ptr& sp)//拷贝构造计数++
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
		{
			++(*_pcount);
		} 
		~shared_ptr()
		{
			cout << "计数--" << endl;
			if (--(*_pcount) == 0) {
				 delete _ptr;
				delete _pcount;
			}
		} 
		
		T* get() const
			
		{
			return _ptr;
		} 
		int use_count() const
		{
			return *_pcount;
		} 
		T& operator*()
		{
			return *_ptr;
		} 
		T* operator->()
		{
			return _ptr;
		}
		shared_ptr&operator=(const shared_ptr&sp) 
		{
			if (sp._ptr != _ptr) {
				if (--(*_pcount) == 0) {
					cout << "计数--" << endl;
					delete _ptr;

					delete _pcount;
				}
				_pcount = sp._pcount;
				_ptr = sp._ptr;
				++(*_pcount);
			}
			return *this;
		}
	private:
		T* _ptr;
		int* _pcount;
	};
}

shared_ptr:的底层原理是利用一个pcount计数指针,每当函数进行拷贝构造或赋值时就++,当pcount==0,即所有指向地址的指针被析构时,pcount--.直到零时delete pcount 和ptr指针。
但是此时我们会出现一个新的问题,我们无法delete 那些特殊类如:[],file等,此时我们就必须再增加一个函数去保证他能析构成功,此时我们就必须引入如下代码:
在类里面添加:
template
shared_ptr(T* ptr, D del) 
	: _ptr(ptr)
	, _pcount(new int(1))
	,_del(del)
{}
再利用functional类里面的封装一个对象void(T*):
function _del = [](T* ptr) {
	delete[] ptr;
};
此时代码就趋近于完善。

有了代码后我们回到上面的问题循环引用:当我们有两个有智能指针管理的类时:

1. 右边的节点什么时候释放呢,左边节点中的_next管着呢,_next析构后,右边的节点就释放了。 
2. _next什么时候析构呢,_next是左边节点的的成员,左边节点释放,_next就析构了。 
3. 左边节点什么时候释放呢,左边节点由右边节点中的_prev管着呢,_prev析构后,左边的节点就释
放了。
4. _prev什么时候析构呢,_prev是右边节点的成员,右边节点释放,_prev就析构了。 
⾄此逻辑上成功形成回旋镖似的循环引⽤,谁都不会释放就形成了循环引⽤,导致内存泄漏
把ListNode结构体中的_next和_prev改成weak_ptr,weak_ptr绑定到shared_ptr时不会增加它的
引⽤计数,_next和_prev不参与资源释放管理逻辑,就成功打破了循环引⽤,解决了这⾥的问题

weak_ptr:weak_ptr构造时不⽀持绑定到资源,只⽀持绑定到shared_ptr,绑定到shared_ptr时,不增加shared_ptr的引⽤计数,那么就可以解决上述的循环引⽤问题。weak_ptr也没有重载operator*和operator->等,因为他不参与资源管理,那么如果他绑定的shared_ptr已经释放了资源,那么他去访问资源就是很危险的。weak_ptr⽀持expired检查指向的资源是否过期,use_count也可获取shared_ptr的引⽤计数,weak_ptr想访问资源时,可以调⽤lock返回⼀个管理资源的shared_ptr,如果资源已经被释放,返回的shared_ptr是⼀个空对象,如果资源没有释放,则通过返回的shared_ptr访问资源是安全的,下面是测试代码:

int main() {
	std::shared_ptr sp1(new string("111111"));
	std::shared_ptr sp2(sp1);
	std::weak_ptr wp = sp1;
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	// sp1和sp2都指向了其他资源,则weak_ptr就过期了
	sp1 = make_shared("222222");
	cout << "2222222" << endl;
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	cout << "3333333" << endl;
	sp2 = make_shared("333333");
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	wp = sp1;
	//std::shared_ptr sp3 = wp.lock();
	auto sp3 = wp.lock();
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	*sp3 += "###";
    cout << *sp1 << endl;
	return 0;
}

今天的更新就到这里了,如有错误,欢迎指出!!!