使用 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;
    }

    // Demonstrates how to test whether the
    // pointed-to memory still exists or not.
    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),
    };

    // Each controller depends on all others not being deleted.
    // Give each controller a pointer to all the others.
    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 { // base class for shared_ptr and weak_ptr

template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource

引用计数类 _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 { // increment use count
_MT_INCR(_Uses);
}

void _Incwref() noexcept { // increment weak reference count
_MT_INCR(_Weaks);
}

// 当引用计数减为 0 时销毁对象
void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}

void _Decwref() noexcept { // decrement weak reference count
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 { // decrement reference count
if (_Rep) {
_Rep->_Decref();
}
}

// 弱引用计数加一
void _Incwref() const noexcept {
if (_Rep) {
_Rep->_Incwref();
}
}

// 弱引用计数减一
void _Decwref() noexcept { // decrement weak reference count
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 {
// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor
_Ptr = _Right._Ptr;
_Rep = _Right._Rep;

_Right._Ptr = nullptr;
_Right._Rep = nullptr;
}

// 通过定义辅助函数来实现 hared_ptr 的拷贝,拷贝后,引用计数加一
template <class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's (converting) copy ctor
_Other._Incref();

_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing ctor
_Other._Incref();

_Ptr = _Px;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing move ctor
_Ptr = _Px;
_Rep = _Other._Rep;

_Other._Ptr = nullptr;
_Other._Rep = nullptr;
}

template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
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 { // implement weak_ptr's ctors
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 {
// implement weak_ptr's copy converting ctor
if (_Other._Rep) {
_Rep = _Other._Rep; // always share ownership
_Rep->_Incwref();

if (_Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_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 {
// implement weak_ptr's move converting ctor
_Rep = _Other._Rep; // always transfer ownership
_Other._Rep = nullptr;

if (_Rep && _Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_Rep->_Decref();
} else {
_STL_INTERNAL_CHECK(!_Ptr);
}

_Other._Ptr = nullptr;
}

weak_ptr 类

weak_ptr 模板类继承于基类指针

template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource

构造函数和析构函数。由于代码过长,使用点省略部分内容,同时具体实现未放进来,具体可以参考 MSVC 的源码。底层指针类型兼容是指可以进行转换。

constexpr weak_ptr() noexcept {}

weak_ptr(const weak_ptr& _Other) noexcept {
this->_Weakly_construct_from(_Other); // same type, no conversion
}

// 通过 shared_ptr 构建 weak_ptr
template <class _Ty2, ...>
weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion
}

// 通过底层指针类型兼容的 weak_ptr 拷贝构造 weak_ptr
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));
}

// 通过底层指针类型兼容的 weak_ptr 移动构造 weak_ptr
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));
}

// 析构函数,弱引用计数为 0 时释放资源
~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;
}

// 底层指针类型兼容,通过 shard_ptr 赋值给 weak_ptr
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 { // release resource, convert to null weak_ptr object
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 { // convert to shared_ptr
shared_ptr<_Ty> _Ret;
(void) _Ret._Construct_from_weak(*this);
return _Ret;
}

参考链接