“循环引用”简单来说就是:两个对象互相使用一个 shared_ptr 成员变量指向对方。这样会引发一个问题,其中任何一个对象的引用计数都为2。析构时两个资源引⽤计数会减⼀,于是两者引⽤计数还是大于0,导致跳出函数时资源没有被释放(离开作用域时两个对象的析构函数没有被调⽤)
看下面的例子:
#include <memory>
using namespace std;
class father;
class son;
class father
{
public:
shared_ptr<son> son;
public:
~father() //析构函数
{
cout << "father distructor" << endl; //删除对象
}
};
class son
{
public:
shared_ptr<father> father;
public:
~son()
{
cout << "son distructor" << endl; //删除对象
}
};
void test(father **f, son **s)
{
shared_ptr<father> pfath(new father());
shared_ptr<son> pson(new son());
*f = pfath.get();
*s = pson.get();
pfath->son = pson;
pson->father = pfath;
cout << "pfath use_count:" << pfath.use_count() << endl; // father对象usecount:2
cout << "pson use_count:" << pson.use_count() << endl; // sond对象usecount:2
}
int main()
{
father *pfather;
son *pson;
test(&pfather, &pson); // test()结束以后析构函数并没有被调用
cout << "pfath use_count:" << pson->father.use_count() << endl; // father对象usecount:1
cout << "pson use_count:" << pfather->son.use_count() << endl; // sond对象usecount:2
return 0;
}
输出
pfath use_count:2
pson use_count:2
pfath use_count:1
pson use_count:1
可以看到test()结束以后析构函数并没有被调用,会导致资源泄露。这显然违背了智能指针的初衷。
解决办法
使用weak_ptr 来打破循环引用,它与一个 shared_ptr 绑定,但却不参与引用计数的计算,不论是否有 weak_ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。
修改上述代码:
class father
{
public:
weak_ptr<son> son; // shared_ptr >> weak_ptr
public:
~father() //析构函数
{
cout << "son distructor" << endl; //删除对象
}
};
class son
{
public:
weak_ptr<father> father; // shared_ptr >> weak_ptr
public:
~son()
{
cout << "father distructor" << endl; //删除对象
}
};
运行输出:
pfath use_count:1
pson use_count:1
father distructor
son distructor
pfath use_count:0
pson use_count:0