C++ | 智能指针面试题:使用weak_ptr解决循环引用无法析构的问题

前言

利用上篇文章的shared_ptr和weak_ptr来解决循环引用。

一.对于普通的shared_ptr

不带weak_ptr的共享型智能指针,如果循环引用,那么就会造成无法析构的问题。

示例代码

class Child;
class Parent
{
public:
	std::shared_ptr<Child> children;
	Parent() { cout << "Parent::Create" << endl; }
	~Parent() { cout << "Parent::Destroy" << endl; }

	void Print() { cout << "Hello world " << endl; }
};

class Child
{
public:
	std::shared_ptr<Parent> parent;
	Child() { cout << "Child::Create" << endl; }
	~Child() { cout << "Child::Destroy" << endl; }
};


void fun()
{
	std::shared_ptr<Parent> p(new Parent());
	std::shared_ptr<Child> c(new Child());

	p->children = c;
	c->parent = p;

	c->parent->Print();
}

int main(void)
{
	fun();
	return 0;
}

初始状态:
p是指向 Parent对象的共享型智能指针,该对象里面含有一个指向Child类型的children共享型智能指针。
c是指向 Child 对象的共享型智能指针,该对象里面含有一个指向Parent类型的 parent 共享型智能指针。

在这里插入图片描述

循环指向后:

在这里插入图片描述

在这里插入图片描述

运行结果:
可以看出对象并没有析构掉。

在这里插入图片描述

二.带有weak_ptr的shared_ptr

若要了解这种指针,可以看这篇文章:
仿写weak_ptr和shared_ptr: 【点此查看】

带weak_ptr的指针内存简图示例:

在这里插入图片描述

示例代码

class Child;
class Parent
{
public:
	my_weak_ptr<Child> children;
	Parent() { cout << "Parent::Create" << endl; }
	~Parent() { cout << "Parent::Destroy" << endl; }

	void Print() { cout << "Hello world " << endl; }
};

class Child
{
public:
	my_weak_ptr<Parent> parent;
	Child() { cout << "Child::Create" << endl; }
	~Child() { cout << "Child::Destroy" << endl; }
};


void fun()
{
	my_shared_ptr<Parent> p(new Parent());
	my_shared_ptr<Child> c(new Child());

	p->children = c;
	c->parent = p;

	c->parent.lock()->Print();
}

int main(void)
{
	fun();
	return 0;
}

运行结果:
在这里插入图片描述

1.构造过程图示

在完成初始化时,如图:

  1. 共享型智能指针p的成员_Ptr指向一个Parent类型的对象,_Rep成员指向一个引用计数对象,该对象的_Ptr也指向Parent类型的对象,并将引用计数都置为1。

  2. 同理,共享型智能指针c的成员_Ptr指向一个Child类型的对象,_Rep指向引用计数对象,引用计数的_Ptr也指向Child这一对象,引用计数都置为1。

在这里插入图片描述

2.相互引用后图示

在互相引用后:
p指向的Parent对象里有一个 Child类型的weak_ptr指针 children,让该指针指向c所指向的引用计数类型,并将_Weaks(弱引用计数)加一。

c指向的Child对象里有一个Parent类型的weak_ptr指针parent,让该指针指向p所指向的引用计数类型,并将这个引用计数类型的_Weaks加一。

在这里插入图片描述

3.析构过程图示

先来看看shared_ptr和weak_ptr的析构函数

~my_shared_ptr()::

~my_shared_ptr()
{
	if (_Rep != nullptr && --_Rep->_Uses == 0)
	{
		_mDeletor(_Ptr);
		if (--_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
	}
	_Ptr = nullptr;
	_Rep = nullptr;
}

~my_weak_ptr::

~my_weak_ptr()
{
	if (_Rep != nullptr && --_Rep->_Weaks == 0)
	{
		delete _Rep;
	}
	_Rep = nullptr;	
}

由于指针c是后创建的,所以先析构c:

  1. 先判断_Rep指针是否为nullptr,若不为nullptr,则对_Uses这个引用计数减一;
  2. 减完_Uses后为0,所以这时候要对_Ptr指向的这个Child对象析构;
  3. 但这个Child对象的成员有个weak_ptr指向指针p的引用计数结构,所以在销毁该对象前,需要先将p的引用计数结构里的_Weaks(弱引用计数)减一。
  4. 再对本身所指向的_Weaks(弱引用计数)减一,若为0,则对整个计数结构删除,否则不做处理;
  5. 再返回到析构Child类型的对象这一步,将其析构
  6. 最后退回到指针c的结构,将指针c的_Ptr和_Rep置为nullptr。

流程:
在这里插入图片描述

变化后:
在这里插入图片描述

析构p的过程:

  1. 若_Rep不为nullptr,再对引用计数_Uses减一,减一后为0了;
  2. 此时_Uses为0,则析构Parent类型的对象,它里面的成员有一个weak_ptr(需要调用weak_ptr的析构函数),指向c之前的引用计数结构(现在和c没关系了),那么先进入这个结构,对_Weaks(弱引用计数)减一;此时弱引用计数为0,需要对整个计数结构析构。
  3. 删除结构后返回p的析构函数,将Parent对象析构;
  4. 再对弱引用计数减一(_Weaks),删除后为0,则析构这个计数结构(delete _Rep)。
  5. 最后将p的 _Ptr和 _Ref置为nullptr。

图示:
流程
在这里插入图片描述

结果:
在这里插入图片描述

END

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值