欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

智能指针的循环引用和如何解决循环引用

程序员文章站 2022-07-12 13:51:48
...

首先咱们先看一个程序(如下):

#include<iostream>
#include<memory>
using namespace std;
template<class T>
struct Node
{
	Node(T data = T()) :_data(data), Pnext(NULL), Ppre(NULL)
	{
	}
	~Node()
	{
		cout << "Call ~Node()" << endl;
	}
	shared_ptr<Node> Pnext;
	shared_ptr<Node> Ppre;
	T _data;
};
void testShared_Ptr()
{
	shared_ptr<Node<int>>sp1(new Node<int>(1));
	shared_ptr<Node<int>>sp2(new Node<int>(2));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	sp1->Pnext = sp2;
	sp2->Ppre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	testShared_Ptr();                                                                                                                cout<<"testShared_pte()函数运行结束"<<endl;
	return 0;
}

我们在testShared_ptr函数里面开辟了两个结点,并分别交给两个Shared_ptr指针保管,我们打印了一下他们的引用计数结果如下图:

智能指针的循环引用和如何解决循环引用

然后我们让sp1和结点node2里面的Ppre共同管理着结点node1(他们共用同一个引用计数),让sp2和node1里面的pNext共通过管理着node2(他们共用同一个引用计数);

智能指针的循环引用和如何解决循环引用


此时我们来看一下两个智能指针的引用计数(如下图):

智能指针的循环引用和如何解决循环引用

他们已经由1变为2了,说明每个结点都有两个智能指针管理着,当我们继续往下执行时,会发现testShared_ptr()函数已经执行结束了,但是结点的析构函数并没有调用,我们开辟的结点,并没有被释放,运行结果如下图:

智能指针的循环引用和如何解决循环引用

通过下图,我们来分析一下为什么这两个结点没有被释放:

智能指针的循环引用和如何解决循环引用

智能指针的循环引用和如何解决循环引用

为了解决shared_ptr引起的循环引用问题,我们引入一个weak_ptr指针,他不能单独使用,只能配合shared_ptr使用。

#include<iostream>
#include<memory>
using namespace std;
template<class T>
struct Node
{
	Node(T data = T()) :_data(data)
	{
	}
	~Node()
	{
		cout << "Call ~Node()" << endl;
	}
	weak_ptr<Node> Pnext;
	weak_ptr<Node> Ppre;
	T _data;
};
void testShared_Ptr()
{
	shared_ptr<Node<int>>sp1(new Node<int>(1));
	shared_ptr<Node<int>>sp2(new Node<int>(2));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	sp1->Pnext = sp2;
	sp2->Ppre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	testShared_Ptr();
	cout << "testShared_pte()函数运行结束"<<endl;
	return 0;
}

我们把第一个代码Node类的成员和构造函数调整了一下,看一下运行结果,还存在循环引用不(结果如下图):

智能指针的循环引用和如何解决循环引用

通过上图我们可以看出,我们开辟的结点已经成功释放了,shared_ptr引起的循环引用已经得到解决了,但是具体是怎么解决的我们来慢慢分析,首先我们先看一下库函数里面的shared_ptr到底是怎么实现的,跟我们模拟实现的到底在哪不一样(分析如下图):

智能指针的循环引用和如何解决循环引用



通过上图我们可以看到他继承了一个Ptr_base的类,这个类里面有一个T类型的_Ptr指针,还有一个负责引用计数的类类型的指针,现在我们去看这个负责引用计数的类是怎么实现的,他都有那些成员变量(如下图):

智能指针的循环引用和如何解决循环引用

从上图我们可得到_ptr_base类里面的是第二个成员变量是一个引用计数的基类的指针,他是一个抽象类,里面有两个long类型的变量uses和weak,从他们的名字看,可以猜测他们一个是shared_ptrd的引用计数,一个是weak_ptr的引用计数,他里面有两个纯虚函数,一个是释放他们所管理的资源的,一个是释放自己的,他有三个派生类,分别对这两个函数进行了定义。可以用基类指针指向他的派生类,来达到用正确的方式去释放他们管理的资源和他本身所占用的资源。

接下来咱们看一下下面代码干了什么事情:

	sp1->Pnext = sp2;
	sp2->Ppre = sp1;                                                             
单步前:
智能指针的循环引用和如何解决循环引用

单步后:

智能指针的循环引用和如何解决循环引用

从上图我们可以看出,_uses并没有增加,而weaks增加了1。模型图如下:

智能指针的循环引用和如何解决循环引用



我们继续单步,当出了testshared_ptr()函数作用域之前,我们看看智能指针如何被释放的,如下图:

智能指针的循环引用和如何解决循环引用

以上就是我对智能指针shared_ptr造成的循环引用的理解,以及底下是如何解决的,如果有不对的地方,还望指出,十分感谢。