C++11:shared_ptr循环引用问题

一、shared_ptr的弊端

cpp 复制代码
struct Listnode
{
	int _val;
	std::shared_ptr<Listnode> _prev;
	std::shared_ptr<Listnode> _next;
	Listnode(int val )
		:_val(val)
		,_prev(nullptr)
		,_next(nullptr)
	{}
	~Listnode()
	{
		cout << "~Listnode()" << endl;
	}
};
int main()
{
	std::shared_ptr<Listnode> n1(new Listnode(20));
	std::shared_ptr<Listnode> n2(new Listnode(10));

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

	return 0;
}

如上的情况就出现了循环引用,两个对象相互掣肘。

  1. node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
  2. node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
  3. node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
  4. 也就是说_next析构了,node2就释放了。
  5. 也就是说_prev析构了,node1就释放了。
  6. 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放。
    展开来讲:
    两个对象的引用计数都变为2,当要进行析构时就陷入了循环引用。

1、左边节点要想析构就得等右边的_prev去调用析构。

2、右边节点被delete时去调用_prev.

3、而右边节点被析构需要左边节点的_next去调用析构

4、左边节点被delete时去调用_prev

5、左边节点要想析构就得等右边的_prev去调用析构。

到这里就以及死循环了。

二、weak

它的特点就是不增加引用计数,专门用于shared_ptr出现循环引用的情况。

cpp 复制代码
struct Listnode
{
	int _val;
	std::weak_ptr<Listnode> _prev;
	std::weak_ptr<Listnode> _next;
	Listnode(int val )
		:_val(val)
	{}
	~Listnode()
	{
		cout << "~Listnode()" << endl;
	}
};
int main()
{
	std::shared_ptr<Listnode> n1(new Listnode(20));
	std::shared_ptr<Listnode> n2(new Listnode(10));

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

	return 0;
}

2.1模拟实现

cpp 复制代码
struct Listnode
{
	int _val;
	gaz::weak_ptr<Listnode> _prev;
	gaz::weak_ptr<Listnode> _next;
	Listnode(int val )
		:_val(val)
	{}
	~Listnode()
	{
		cout << "~Listnode()" << endl;
	}
};
int main()
{
	gaz::shared_ptr<Listnode> n1(new Listnode(20));
	gaz::shared_ptr<Listnode> n2(new Listnode(10));

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

	return 0;
}
cpp 复制代码
template<class T>
	class weak_ptr
	{
	public:
		//RALL
		weak_ptr()
			:_ptr(nullptr)
		{}
		weak_ptr(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
		}
		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
相关推荐
Predestination王瀞潞1 小时前
IO操作(Num22)
开发语言·c++
haoly19892 小时前
数据结构和算法篇-线性查找优化-移至开头策略
数据结构·算法·移至开头策略
宋恩淇要努力3 小时前
C++继承
开发语言·c++
沿着路走到底4 小时前
python 基础
开发语言·python
沐知全栈开发5 小时前
C# 委托(Delegate)
开发语言
江公望5 小时前
Qt qmlRegisterSingletonType()函数浅谈
c++·qt
任子菲阳5 小时前
学Java第三十四天-----抽象类和抽象方法
java·开发语言
学Linux的语莫6 小时前
机器学习数据处理
java·算法·机器学习
csbysj20206 小时前
如何使用 XML Schema
开发语言
R6bandito_6 小时前
STM32中printf的重定向详解
开发语言·经验分享·stm32·单片机·嵌入式硬件·mcu