C++ 智能指针的循环引用问题

        首先我们要知道,循环引用问题是shared_ptr智能指针引起的。shared_ptr的一个最大的陷阱是循环引用,循环引用会导致堆内存无法正确释放,导致内存泄漏。

那么shared_ptr是如何引起循环引用的呢?

先明确一个结论:shared_ptr智能指针管理的对象,只有在引用计数减为0的时候才会释放。

而循环引用发生的情况就是违反了上面所说的结论:引用计数不能递减到0,以至于对象不能释放。

shared_ptr的引用计数机制是这样的:

shared_ptr是多个智能指针指向相同的对象。引用计数就是标记同一块内存同时有多少个shared_ptr智能指针对象指向这个堆内存地址;通过赋值、拷贝等可以让引用计数增加;当一个指向某个内存的智能指针对象被销毁的时候,指向这个内存的所有智能指针的引用计数都减一;当只有一个智能指针对象指向一个内存地址(引用计数为1)也要被销毁的时候,该内存地址就会被系统自动回收。(其实是shared_ptr的析构函数所做的工作是:先将与该智能指针对象共享同一内存地址的所有智能指针对象的引用计数减一[其实,这个引用计数就是在该共享内存上面存着的,只要其中一个对象去修改,那么所有指向它的对象的引用计数就全都改了],减一之后判断是不是0,如果大于零,就不管别的只用销毁该对象即可;如果等于0,说明已经没有任何对象指向该内存空间,所以就要进行堆空间的回收)

举个栗子:

#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;//1
	cout << sp2.use_count() << endl;//1
	sp1->Pnext = sp2;
	sp2->Ppre = sp1;
	cout << sp1.use_count() << endl;//2
	cout << sp2.use_count() << endl;//2
}
int main()
{
	testShared_Ptr();                                                                                                                            
    cout<<"testShared_pte()函数运行结束"<<endl;
	return 0;
}

我们在testShared_ptr函数里面开辟了两个结点,并分别交给两个Shared_ptr指针保管,他们的引用计数都是1

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

此时两个智能指针的引用计数都变为了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
	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;
}

weak_ptr不能控制它所指向对象的生存期。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,那么对象就会被释放。

根据这个weak_ptr的特性,可以完美地解决shared_ptr出现的循环引用问题。

 

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值