使用 shared_ptr 可能会导致循环引用的问题。weak_ptr 能够解决循环引用的问题。
weak_ptr 能够访问 shared_ptr 的底层对象,但不会增加该对象的引用计数。
可以通过 weak_ptr 创建一个 shared_ptr,来保持 shared_ptr 底层对象的生命周期,从而防止 weak_ptr 在使用时,对象被释放掉。
Example
使用 weak_ptr 解决循环引用问题
#include <iostream> #include <memory> #include <string> #include <vector> #include <algorithm> using namespace std;class Controller { public : int Num; wstring Status; vector<weak_ptr<Controller>> others; explicit Controller (int i) : Num(i), Status(L"On" ) { wcout << L"Creating Controller" << Num << endl; } ~Controller () { wcout << L"Destroying Controller" << Num << endl; } void CheckStatuses () const { for_each(others.begin (), others.end (), [](weak_ptr<Controller> wp) { auto p = wp.lock (); if (p) { wcout << L"Status of " << p->Num << " = " << p->Status << endl; } else { wcout << L"Null object" << endl; } }); } }; void RunTest () { vector<shared_ptr<Controller>> v{ make_shared <Controller>(0 ), make_shared <Controller>(1 ), make_shared <Controller>(2 ), make_shared <Controller>(3 ), make_shared <Controller>(4 ), }; for (int i = 0 ; i < v.size (); ++i) { for_each(v.begin (), v.end (), [&v, i](shared_ptr<Controller> p) { if (p->Num != i) { v[i]->others.push_back (weak_ptr <Controller>(p)); wcout << L"push_back to v[" << i << "]: " << p->Num << endl; } }); } for_each(v.begin (), v.end (), [](shared_ptr<Controller> &p) { wcout << L"use_count = " << p.use_count () << endl; p->CheckStatuses (); }); } int main () { RunTest (); wcout << L"Press any key" << endl; char ch; cin.getline (&ch, 1 ); }
代码解读(MSVC SDK 10.0.19041.0) weak_ptr 是类模板,其原型为
template < class T > class weak_ptr ;
在 MSVC 的实现中,带有引用计数的类是在一个基类中实现的,然后 shared_ptr 和 weak_ptr 继承该基类。
template <class _Ty >class _Ptr_base { template <class _Ty >class weak_ptr : public _Ptr_base<_Ty> {
引用计数类 _Ref_count_base 引用计数在智能指针的用法是当有新的 shared_ptr 共享对象时,对象的引用计数加 1,当有指向对象的指针释放时,引用计数减 1.
在引用计数类中,有两个成员变量,一个是引用的数量,一个是 weak 使用的数量。
using _Atomic_counter_t = unsigned long ;_Atomic_counter_t _Uses = 1 ; _Atomic_counter_t _Weaks = 1 ;
引用计数类支持一些对计数的操作
void _Incref() noexcept { _MT_INCR(_Uses); } void _Incwref() noexcept { _MT_INCR(_Weaks); } void _Decref() noexcept { if (_MT_DECR(_Uses) == 0 ) { _Destroy(); _Decwref(); } } void _Decwref() noexcept { if (_MT_DECR(_Weaks) == 0 ) { _Delete_this(); } } long _Use_count() const noexcept { return static_cast <long >(_Uses); } virtual void * _Get_deleter(const type_info&) const noexcept { return nullptr ; }
指针基类 _Ptr_base _Ptr_base 实现了 shared_ptr 和 weak_ptr 的基本操作。
在 _Ptr_base 基类中,有两个成员变量,底层指针 _Ptr 和引用计数类 _Rep
element_type* _Ptr{nullptr }; _Ref_count_base* _Rep{nullptr };
然后定义了对指针和引用计数的操作
void _Incref() const noexcept { if (_Rep) { _Rep->_Incref(); } } void _Decref() noexcept { if (_Rep) { _Rep->_Decref(); } } void _Incwref() const noexcept { if (_Rep) { _Rep->_Incwref(); } } void _Decwref() noexcept { if (_Rep) { _Rep->_Decwref(); } } _NODISCARD long use_count () const noexcept { return _Rep ? _Rep->_Use_count() : 0 ; } _NODISCARD element_type* get () const noexcept { return _Ptr; }
基类指针中还定义了一些构造函数
_Ptr_base(const _Ptr_base&) = delete ; _Ptr_base& operator =(const _Ptr_base&) = delete ; constexpr _Ptr_base() noexcept = default ;~_Ptr_base() = default ; template <class _Ty2 >void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept { _Ptr = _Right._Ptr; _Rep = _Right._Rep; _Right._Ptr = nullptr ; _Right._Rep = nullptr ; } template <class _Ty2 >void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept { _Other._Incref(); _Ptr = _Other._Ptr; _Rep = _Other._Rep; } template <class _Ty2 >void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept { _Other._Incref(); _Ptr = _Px; _Rep = _Other._Rep; } template <class _Ty2 >void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept { _Ptr = _Px; _Rep = _Other._Rep; _Other._Ptr = nullptr ; _Other._Rep = nullptr ; } template <class _Ty2 >bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept { if (_Other._Rep && _Other._Rep->_Incref_nz()) { _Ptr = _Other._Ptr; _Rep = _Other._Rep; return true ; } return false ; } template <class _Ty2 >void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { if (_Other._Rep) { _Ptr = _Other._Ptr; _Rep = _Other._Rep; _Rep->_Incwref(); } else { _STL_INTERNAL_CHECK(!_Ptr && !_Rep); } } template <class _Ty2 >void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept { if (_Other._Rep) { _Rep = _Other._Rep; _Rep->_Incwref(); if (_Rep->_Incref_nz()) { _Ptr = _Other._Ptr; _Rep->_Decref(); } else { _STL_INTERNAL_CHECK(!_Ptr); } } else { _STL_INTERNAL_CHECK(!_Ptr && !_Rep); } } template <class _Ty2 >void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept { _Rep = _Other._Rep; _Other._Rep = nullptr ; if (_Rep && _Rep->_Incref_nz()) { _Ptr = _Other._Ptr; _Rep->_Decref(); } else { _STL_INTERNAL_CHECK(!_Ptr); } _Other._Ptr = nullptr ; }
weak_ptr 类 weak_ptr 模板类继承于基类指针
template <class _Ty >class weak_ptr : public _Ptr_base<_Ty> {
构造函数和析构函数。由于代码过长,使用点省略部分内容,同时具体实现未放进来,具体可以参考 MSVC 的源码。底层指针类型兼容是指可以进行转换。
constexpr weak_ptr () noexcept {}weak_ptr (const weak_ptr& _Other) noexcept { this ->_Weakly_construct_from(_Other); } template <class _Ty2 , ...>weak_ptr (const shared_ptr<_Ty2>& _Other) noexcept { this ->_Weakly_construct_from(_Other); } template <class _Ty2 , ...>weak_ptr (const weak_ptr<_Ty2>& _Other) noexcept { this ->_Weakly_convert_lvalue_avoiding_expired_conversions(_Other); } weak_ptr (weak_ptr&& _Other) noexcept { this ->_Move_construct_from(_STD move (_Other)); } template <class _Ty2 , enable_if_t <_SP_pointer_compatible<_Ty2, _Ty>::value, int > = 0 >weak_ptr (weak_ptr<_Ty2>&& _Other) noexcept { this ->_Weakly_convert_rvalue_avoiding_expired_conversions(_STD move (_Other)); } ~weak_ptr () noexcept { this ->_Decwref(); }
赋值函数
weak_ptr& operator =(const weak_ptr& _Right) noexcept { weak_ptr (_Right).swap (*this ); return *this ; } template <class _Ty2 >weak_ptr& operator =(const weak_ptr<_Ty2>& _Right) noexcept { weak_ptr (_Right).swap (*this ); return *this ; } weak_ptr& operator =(weak_ptr&& _Right) noexcept { weak_ptr (_STD move (_Right)).swap (*this ); return *this ; } template <class _Ty2 >weak_ptr& operator =(weak_ptr<_Ty2>&& _Right) noexcept { weak_ptr (_STD move (_Right)).swap (*this ); return *this ; } template <class _Ty2 >weak_ptr& operator =(const shared_ptr<_Ty2>& _Right) noexcept { weak_ptr (_Right).swap (*this ); return *this ; }
weak_ptr 也支持 reset 和 swap。由于 weak_ptr 不占有资源,所以可能会出现一种情况,就是在weak_ptr 还在使用时,原始的 shared_ptr 指向的资源已经被释放了,则会使得 weak 的行为不正确。因此,weak_ptr 提供了将自身转换为 shared_ptr 的方法,使得对象的生命周期被扩展到 weak_ptr 结束。
void reset () noexcept { weak_ptr{}.swap (*this ); } void swap (weak_ptr& _Other) noexcept { this ->_Swap(_Other); } _NODISCARD bool expired () const noexcept { return this ->use_count () == 0 ; } _NODISCARD shared_ptr<_Ty> lock () const noexcept { shared_ptr<_Ty> _Ret; (void ) _Ret._Construct_from_weak(*this ); return _Ret; }
参考链接