您的位置:首页 > 其它

weak_ptr打破环状引用

2015-10-08 16:44 337 查看
    weak_ptr是一种不控制对象生存周期的智能指针,它指向一个shared_ptr管理的对象...它不会改变shared_ptr的引用计数——《C++ Primer .5th》。很明显,weak_ptr的特点是“弱引用”。有什么用?应用场景是什么地方?

    考虑它的胞兄shared_ptr,每次复制之后引用计数加一,不再指向对象(自身销毁或指向其他对象)时时引用计数减一,减至零值调用所管理资源对象的析构函数。考虑这样一个情况,两个对象各自包含指向彼此的shared_ptr成员,形成环状引用,引用计数永远为1,不能销毁,造成内存泄漏。

//x86_64 win7_64 vs2013 ultimate
#include <iostream>
#include <string>
#include <memory>
using namespace std;

class A;
class B;

class A{
public:
~A(){ cout << "destroying A\n" ; }
shared_ptr<B> pb;
};

class B{
public:
~B(){ cout << "destroying B\n" ; }
shared_ptr<A> pa;
};

void test(){
shared_ptr<A> a(new A());
shared_ptr<B> b(new B());

a->pb = b;
b->pa = a;
}

int main(){
cout << "begin test...\n";
test();
cout << "end test\n";
}

output:
begin test...
end test
请按任意键继续. . .
 
    在函数test()中便是刚才提到的情形,A、B的对象分别被彼此的shared_ptr所引用,离开test()作用域后,A、B对象再也不能被用户访问,因为智能指针a、b被销毁了,但是各自引用计数为1,两个对象的内存再也不会被释放,可怕的内存泄漏就此产生。

    那么weak_ptr对此又有帮助呢?考虑如果将B中的智能指针换成weak_ptr<A>,回过头看test()里发生了什么?首先shared_ptr版本的a、b初始化,引用计数各自为1,然后将b赋值给A中的shared_ptr,B对象引用计数变为2,将a赋值给B中的weak_ptr,此操作不改变shared_ptr<A>的引用计数,依旧为1。退出test作用域,a、b析构,B引用计数变为1,A的引用计数为0。A的析构函数被调用,A中的shared_ptr<B>也被销毁,即B的引用计数再次减1变为0,调用B的析构函数。此时,内存已经被全部回收。

class B{
public:
~B(){ cout << "destroying B\n" << endl; }
weak_ptr<A> pa;
};
output:
begin test...
destroying A

destroying B

end test
请按任意键继续. . .

    实际用用的例子,想象在一个tree结构中,父节点通过一个共享所有权的引用(chared_ptr)引用子节点,同时子节点又必须持有父节点的引用。如果这第二个引用也共享所有权,就会导致一个循环,最终两个节点内存都无法释放。

以下是weak_ptr的接口函数:

(constructor)Construct weak_ptr (public member function)(destructor)Destroy weak_ptr (public member function)operator=weak_ptr assignment (public member function)swapSwap content (public member function)resetReset pointer (public member function)use_countUse count (public member function)expiredCheck if expired (public member function)lockLock and restore weak_ptr (public member function)owner_beforeOwner-based ordering (public member function template)

    因为weak_ptr和对象生存周期无关,所以我们在操作一个此时并不清楚对象是否存在(未析构)情况时特别有用:

weak_ptr<int> wpi;
void f(){
if (auto p = wpi.lock()){
cout << *p << endl;
}
else{
cout << "wpi is expired.\n" ;
}
}
int main(){
{
auto sp = make_shared<int>(42);
wpi = sp;
f();
}
f();
}
output:
42
wpi is expired.
请按任意键继续. . .


    由此可以推测出,在多线程编程中,当多个线程操作共享的资源对象,weak_ptr扮演着非常重要的角色。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: