3.4 weak_ptr

weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个“助手”,而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,它像旁观者那样观测资源的使用情况。

3.4.1 类摘要

weak_ptr的类摘要如下:

weak_ptr的接口很小,正如它的名字,它是一个“弱”指针,但它能够完成一些特殊的工作,足以证明它的存在价值。

3.4.2 用法

weak_ptr被设计为与shared_ptr协同工作,可以从一个shared_ptr或另一个weak_ptr对象构造以获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,weak_ptr析构时也不会导致引用计数减少,它只是一个“静静的观察者”。

使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但expired()更快,它表示被观测的资源(也就是被shared_ptr管理的资源)已经不复存在。

weak_ptr没有重载operator*和->,因为它不共享指针,不能操作资源,这正是它“弱”的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,把“弱”关系转换为“强”关系,从而操作资源。但当expired()==true时,lock()函数将返回一个存储空指针的shared_ptr。

下面的代码示范了weak_ptr的用法:

3.4.3 对象自我管理

weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr供外界使用。

这个解决方案是一种惯用法,在头文件<boost/enable_shared_from_this.hpp>里定义一个助手类enable_shared_from_this<T>,它的声明摘要如下:

使用weak_ptr的时候只需要让想被shared_ptr管理的类继承它即可,成员函数shared_from_this()会返回this指针的shared_ptr。例如:

需要注意的是千万不能对一个普通对象(非shared_ptr管理的对象)使用shared_from_this()获取shared_ptr。例如:

这样虽然在语法上正确,编译也无问题,但在运行过程中会导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为。

3.4.4 打破循环引用

有时代码中可能会出现循环引用,这时shared_ptr的引用计数机制就会失效,导致无法正确释放资源,例如:

在上述代码中,两个节点对象互相持有对方的引用,每一个shared_ptr的引用计数都是2,因此在析构时引用计数没有减至0,不会调用删除操作,不会导致内存泄漏。

这时我们可以使用weak_ptr,因为它不会增加智能指针的引用计数,这样就把原来的“强”引用改成“弱”引用,在可能存在循环引用的地方打破了循环,而在真正需要shared_ptr的时候调用weak_ptr的lock()函数: