智能指针的循环引用和如何解决循环引用
首先咱们先看一个程序(如下):
#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造成的循环引用的理解,以及底下是如何解决的,如果有不对的地方,还望指出,十分感谢。
上一篇: Web安全 渗透测试人员——必备的Linux基础知识
下一篇: Linux 新手必备基础命令
推荐阅读
-
vue 解决循环引用组件报错的问题
-
解决ASP.NET MVC返回的JsonResult 中 日期类型数据格式问题,和返回的属性名称转为“驼峰命名法”和循环引用问题
-
详解如何解决使用JSON.stringify时遇到的循环引用问题
-
Python中循环引用(import)失败的解决方法
-
iOS开发“强弱共舞——weak和strong配套使用解决block循环引用问题
-
智能指针的循环引用和如何解决循环引用
-
NSTimer循环引用的几种解决方案
-
有关ios中循环引用问题的分析和解决
-
详解iOS 用于解决循环引用的block timer
-
魔幻离现实仅一步之遥:细说Python的循环调用、循环引用和循环导入