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;
	};
相关推荐
白熊1882 分钟前
【推荐算法】DeepFM:特征交叉建模的革命性架构
算法·架构·推荐算法
L_cl4 分钟前
【Python 算法零基础 4.排序 ⑪ 十大排序算法总结】
python·算法·排序算法
小刘不想改BUG14 分钟前
LeetCode 70 爬楼梯(Java)
java·算法·leetcode
qq_4335545417 分钟前
C++ list代码练习、set基础概念、set对象创建、set大小操作
开发语言·c++·list
老歌老听老掉牙23 分钟前
使用 SymPy 进行向量和矩阵的高级操作
python·线性代数·算法·矩阵·sympy
张伯毅30 分钟前
Java 类型参数 T、R 、 O 、K、V 、E 、? 区别
java·开发语言
lifallen32 分钟前
Flink checkpoint
java·大数据·算法·flink
AI+程序员在路上34 分钟前
单元测试与QTestLib框架使用
开发语言·c++·单元测试
比特森林探险记1 小时前
Go 中的 Map 与字符处理指南
c++·算法·golang
whoarethenext1 小时前
使用 C/C++ 和 OpenCV 实现滑动条控制图像旋转
c语言·c++·opencv