📌本文采用wolai制作,原文 链接
前文介绍了 std::shared_ptr, 注意到在中解释 enable_shared_from_this的实现原理中,提到了weak_ptr
。今天继续看weak_ptr
的实现。
1 什么是weak_ptr? 有什么作用?·
相比 std::unique_ptr和std::shared_ptr,std::weak_ptr
的使用场景比较少。除了 enable_shared_from_this
中使用到以外,std::weak_ptr
最长用的场景是解除shared_ptr的循环依赖问题。
举个例子:
1 |
|
A和B对象相互易用,这导致main函数退出时,a和b对象没有析构,两个析构函数都没有打印,导致内存泄露。为了解决这个问题,可引入weak_ptr
1 |
|
这是因为 std::weak_ptr
允许你持有一个对 std::shared_ptr
管理的对象的非拥有(non-owning)引用。它不会增加引用计数,从而避免了循环引用的问题。
2 类图·
classDiagram class __weak_ptr~_Tp~ { element_type *_M_ptr; // Contained pointer. __weak_count<_Lp> _M_refcount; // Reference counter. } class weak_ptr~_Tp~ { } class __weak_count~_Lp~ { _Sp_counted_base<_Lp> *_M_pi; } class _Sp_counted_base~_Lp~ { _Atomic_word _M_use_count; // #shared _Atomic_word _M_weak_count; // #weak + (#shared != 0) } __weak_ptr <|-- weak_ptr __weak_count--o __weak_ptr _Sp_counted_base --* __weak_count
3 源码·
weak_ptr
的定义如下:
1 | template <typename _Tp> class weak_ptr : public __weak_ptr<_Tp> { |
看起来和shared_ptr
类似,有一个父类。
3.1 __weak_ptr
·
1 | template <typename _Tp, _Lock_policy _Lp> class __weak_ptr { |
和 __shared_ptr
非常类似,有一个指向管理对象的raw pointer _M_ptr
和引用引用计数对象 _M_refcount
3.1.1 构造函数 ·
3.1.1.1 从shared_ptr
中构造·
1 | template <typename _Yp, typename = _Compatible<_Yp>> |
初始化 _M_ptr
和 _M_refcount
。
看下 _M_refcount
是如何构造的:
1 | template <_Lock_policy _Lp> class __weak_count { |
__weak_count
本质上是一个指向控制块的指针,在初始化时,将控制块中的weak的引用计数部分+1。
回顾下控制块中的内容:
1
2
3
4
5
6
7
8
9
10
11 template <_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp> {
public:
_Sp_counted_base() noexcept
: _M_use_count(1), _M_weak_count(1) {}
...
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
};
3.1.2 _M_assign函数·
这里再分析_M_assign
函数,之前在解释 enable_shared_from_this原理时,看了这个函数,这里重新再看下。
1 | // __weak_ptr:: |
__ptr
为继承自enable_shared_from_this
的类的对象__ref_count
为shared_ptr
的控制块计数。
上述代码表示,如果当前weak_ptr
是空(没有控制块),或者指向的控制块的强引用是0, 则将管理指针和控制块计数重新复制。
📌思考: 如果
weak_ptr
不是空,曾经指向过其他控制块,该控制块有强引用,此时调用_M_assign
岂不是没有作用?目前回答:_M_assign
是private的函数,是enable_shared_from_this
的友元函数,所以能够调用本函数的,一定是很清楚weak_ptr
的内部机制的,同时目前笔者看到的只有在enable_shared_from_this
中才会用到,enable_shared_from_this
的weak_ptr只有在shared_ptr
构造的时候才会初始化,此时的used_count
一定是0,所以也不存在问题。但是如果这样思考,为什么不加个assert
或者抛异常?
3.1.3 expired·
Returns whether the weak_ptr object is either empty or there are no more shared_ptr in the owner group it belongs to.
1 | // __weak_ptr实现: |
3.1.4 lock·
Returns a shared_ptr with the information preserved by the weak_ptr object if it is not expired.
1 | // __weak_ptr |
这个转到__shared_ptr
用__weak_ptr
构造函数:
本段为引用:
除了raw pointer和 拷贝、移动构造以外,还可以从
weak_ptr
构造:
1
2
3
4
5
6
7
8
9
10
11 /**
* @brief Constructs a %shared_ptr that shares ownership with @a __r
* and stores a copy of the pointer stored in @a __r.
* @param __r A weak_ptr.
* @post use_count() == __r.use_count()
* @throw bad_weak_ptr when __r.expired(),
* in which case the constructor has no effect.
*/
template <typename _Yp, typename = _Constructible<const weak_ptr<_Yp> &>>
explicit shared_ptr(const weak_ptr<_Yp> &__r) : __shared_ptr<_Tp>(__r) {}转到
__shared_ptr
1
2
3
4
5
6
7 // This constructor is used by __weak_ptr::lock() and
// shared_ptr::shared_ptr(const weak_ptr&, std::nothrow_t).
__shared_ptr(const __weak_ptr<_Tp, _Lp> &__r, std::nothrow_t)
: _M_refcount(__r._M_refcount, std::nothrow) {
_M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr;
}
__shared_ptr拿到
weak
的_M_refcount
构造 shared_ptr的_M_refcount
。 同时初始化_M_ptr
指针。
只要weak_ptr
的refcount
的强引用不是0,则返回对象指针,否则构造为nullptr
4 总结·
本文档介绍了C++标准库中的std::weak_ptr
类及其在解决循环引用问题中的应用。相比于std::unique_ptr
和std::shared_ptr
,std::weak_ptr
的使用场景较为有限,其中一个主要用途是避免循环引用。本文通过一个简单的示例,展示了当两个对象互相持有对方的std::shared_ptr
时可能导致的内存泄漏问题,并说明了如何使用std::weak_ptr
来打破这种循环,确保对象能够被正确销毁。
文档还深入探讨了std::weak_ptr
的实现细节,包括其类图结构、成员变量、构造函数和重要方法(如expired()
、lock()
等)。特别地,lock()
方法用于获取一个共享所有权的智能指针,只有当std::weak_ptr
有效且未过期时才返回非空结果。此外,讨论还包括了std::weak_ptr
与enable_shared_from_this
类之间的交互方式。