引用计数实现的智能指针循环引用问题
什么情况下会出现循环引用?
简单来说,就是类A中包含了类B的智能指针对象,类B中包含了包含了类A的智能指针对象;
类A和类B的智能指针实例化SPA和SPB,当各自内部的智能指针都指向对方,这个时候就出现了循环引用。
循环引用情况下,实例SPA和SPB的引用计数为2,当各自释放的时候,引用计数减为1,并不会释放A、B的对象实例。
// CirCle.h
class ClassB;
class ClassA {
public:
ClassA() { cout << "ClassA构造" << endl;}
~ClassA() { cout << "ClassA析构" << endl;}
shared_ptr<ClassB> spb_; // ClassA中包含ClassB的智能指针对象
};
class ClassB {
public:
ClassB() { cout << "ClassA构造" << endl;}
~ClassB() { cout << "ClassA析构" << endl;}
shared_ptr<ClassA> spa_; // // ClassB中包含ClassA的智能指针对象
};
// main.cpp
shared_ptr<ClassA> spa(new ClassA()); // spa引用计数为1
shared_ptr<ClassB> spb(new ClassB()); // spb引用计数为1
spa->spb_ = spb; // spb引用计数+1为2
spb->spa_ = spa; // spa引用计数+1为2
cout << spa.use_count() << endl; // 输出为2
cout << spb.use_count() << endl; // 输出为2
// 析构spa时,发现引用计数为2,则减少为1;析构spb时,发现引用计数为2,则减少为1;从而造成资源无法释放。
怎么解决?
在类A和类B中使用弱引用 weak_ptr 对象; weak_ptr不会增加智能指针的资源引用计数。
// CirCle2.h
class ClassB2;
class ClassA2 {
public:
ClassA2() { cout << "ClassA构造" << endl;}
~ClassA2() { cout << "ClassA析构" << endl;}
weak_ptr<ClassB2> spb_;
};
class ClassB2 {
public:
ClassB2() { cout << "ClassA构造" << endl;}
~ClassB2() { cout << "ClassA析构" << endl;}
weak_ptr<ClassA2> spa_;
};
// main.cpp
shared_ptr<ClassA2> spa(new ClassA2()); // spa引用计数为1
shared_ptr<ClassB2> spb(new ClassB2()); // spb引用计数为1
spa->spb_ = spb; // spb引用计数为1
spb->spa_ = spa; // spa引用计数为1
cout << spa.use_count() << endl; // 输出为1
cout << spb.use_count() << endl; // 输出为1
// 析构函数正常释放spa和spb对象,从而正常释放类AB的实例
weak_ptr的使用策略?
- weak_ptr是c++11引入的特性,主要是为了解决
shared_ptr引用循环
问题。它本身是个模板类,但是不能直接生成一个实例,必须通过shared_ptr
或者另一个weak_ptr
来实例化,并且没有重载运算符*
和->
,因此它不具备指针属性,它提供了lock
和expired
函数,通过expired
可以检查对象是否有效,lock
可以返回shared_ptr
;当weak_ptr
空悬,也称失效,则lock
返回的shared_ptr
为空,否则返回的shared_ptr
会使引用计数增加。 - 利用
weak_ptr
有效性检查,可以将weak_ptr
作为观察者,当weak_ptr
失效时可以不用通知这个观察者。