前言
利用上篇文章的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.构造过程图示
在完成初始化时,如图:
-
共享型智能指针p的成员_Ptr指向一个Parent类型的对象,_Rep成员指向一个引用计数对象,该对象的_Ptr也指向Parent类型的对象,并将引用计数都置为1。
-
同理,共享型智能指针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:
- 先判断_Rep指针是否为nullptr,若不为nullptr,则对_Uses这个引用计数减一;
- 减完_Uses后为0,所以这时候要对_Ptr指向的这个Child对象析构;
- 但这个Child对象的成员有个weak_ptr指向指针p的引用计数结构,所以在销毁该对象前,需要先将p的引用计数结构里的_Weaks(弱引用计数)减一。
- 再对本身所指向的_Weaks(弱引用计数)减一,若为0,则对整个计数结构删除,否则不做处理;
- 再返回到析构Child类型的对象这一步,将其析构
- 最后退回到指针c的结构,将指针c的_Ptr和_Rep置为nullptr。
流程:
变化后:
析构p的过程:
- 若_Rep不为nullptr,再对引用计数_Uses减一,减一后为0了;
- 此时_Uses为0,则析构Parent类型的对象,它里面的成员有一个weak_ptr(需要调用weak_ptr的析构函数),指向c之前的引用计数结构(现在和c没关系了),那么先进入这个结构,对_Weaks(弱引用计数)减一;此时弱引用计数为0,需要对整个计数结构析构。
- 删除结构后返回p的析构函数,将Parent对象析构;
- 再对弱引用计数减一(_Weaks),删除后为0,则析构这个计数结构(delete _Rep)。
- 最后将p的 _Ptr和 _Ref置为nullptr。
图示:
流程
结果: