欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

C++2.0shared

时间:2023-04-30

在gnu c++中讨论shared_ptr和weak_ptr只需要讨论__shared_count和__weak_count,另外就是weak_ptr没有定义operator ->和operator *,所以没有想原始指针那样的行为。

首先由简入繁。从简单的weak_ptr开始看起,然后研究shared_ptr

文章目录

类图__weak_count

1、`weak_ptr`->`weak_ptr`:2、`shared_ptr`->`weak_ptr`: __shared_count

1、`raw ptr`->`shared_ptr`2、`shared_ptr`->`shared_ptr`3、`weak_ptr`->`shared_ptr` _Sp_counted_base

1、调用`_M_weak_release()`2、调用`_M_release()` enable_shared_from_this

总结weak_ptr的作用


类图 __weak_count

class __weak_count { ... private: friend class __shared_count<_Lp> ; _Sp_counted_base<_Lp> * _M_pi;};

__weak_count只有一个成员变量,这是一个多态指针。

void _M_swap(__weak_count& __r) noexcept { _Sp_counted_base<_Lp> * __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp;}

可以看到两个weak_prt的交换,实际上交换了相互的_M_pi这个多态指针

weak_ptr没有接受原始指针的构造,是能通过weak_ptr和shared_ptr产生。

1、weak_ptr->weak_ptr:

weak_ptr对weak_ptr:拷贝构造和拷贝赋值

__weak_count(const __weak_count& __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != nullptr) _M_pi->_M_weak_add_ref();}__weak_count& operator=(const __weak_count& __r) noexcept { _Sp_counted_base<_Lp> * __tmp = __r._M_pi; if (__tmp != nullptr) __tmp->_M_weak_add_ref(); if (_M_pi != nullptr) _M_pi->_M_weak_release(); _M_pi = __tmp; return *this;}

拷贝构造:入参的多态指针不为空,则共享的弱引用计数+1

拷贝赋值:入参的多态指针不为空,共享弱引用计数+1

​ 原多态指针不为空就释放,然后交换相互的_M_pi这个多态指针

2、shared_ptr->weak_ptr:

__weak_count(const __shared_count<_Lp> & __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != nullptr) _M_pi->_M_weak_add_ref();}__weak_count& operator=(const __shared_count<_Lp> & __r) noexcept { _Sp_counted_base<_Lp> * __tmp = __r._M_pi; if (__tmp != nullptr) __tmp->_M_weak_add_ref(); if (_M_pi != nullptr) _M_pi->_M_weak_release(); _M_pi = __tmp; return *this;}

拷贝构造:入参的多态指针不为空,则共享的弱引用计数+1

拷贝赋值:入参的多态指针不为空,共享弱引用计数+1

​ 原多态指针不为空就释放,然后交换相互的_M_pi这个多态指针

long _M_get_use_count() const noexcept { return _M_pi != nullptr ? _M_pi->_M_get_use_count() : 0;}

weak_prt的计数,其实是调用多态指针_M_pi的虚函数

但是!

但是!

但是!,上面提到的多态指针_Sp_counted_base<_Lp> * __weak_count::_M_pi;,并没有new的地方,注定它的来源是shared_ptr,上述代码中的对其弱引用计数的修改,实际上是修改的shared_ptr的弱引用计数;对引用计数的获取,实际上是对shared_ptr的引用计数的获取。所以weak_ptr是的shared_ptr附属品。

void _M_swap(__shared_count& __r) noexcept { _Sp_counted_base<_Lp> * __tmp = __r._M_pi; __r._M_pi = _M_pi; _M_pi = __tmp;}

可以看到两个__shared_count的交换,实际上交换了相互的_M_pi这个多态指针,和上面__weak_count的交换一样,因为本就指向同一块内存,同一个(堆)对象。

__shared_count 1、raw ptr->shared_ptr

由一颗原始指针去构造shared_ptr是一切的开始:

__shared_count(_Ptr __p) : _M_pi(0) { __try { _M_pi = new _Sp_counted_ptr<_Ptr, _Lp> (__p); } __catch(...) { delete __p; __throw_exception_again; }}

将原始指针用于堆对象_Sp_counted_ptr的构造,多态指针_M_pi将其引用,_Sp_counted_ptr从此开始维护引用计数和RAII

2、shared_ptr->shared_ptr

__shared_count(const __shared_count& __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != 0) _M_pi->_M_add_ref_copy();}__shared_count& operator=(const __shared_count& __r) noexcept { _Sp_counted_base<_Lp> * __tmp = __r._M_pi; if (__tmp != _M_pi) { if (__tmp != 0) __tmp->_M_add_ref_copy(); if (_M_pi != 0) _M_pi->_M_release(); _M_pi = __tmp; } return *this;}

拷贝构造:入参的多态指针不为空,则共享的引用计数+1

拷贝赋值:入参的多态指针不为空,共享弱引用计数+1

​ 原多态指针不为空就释放,然后交换相互的_M_pi这个多态指针

3、weak_ptr->shared_ptr

__shared_count(const __weak_count& __r) : _M_pi(__r._M_pi) { if (_M_pi != nullptr) _M_pi->_M_add_ref_lock(); else __throw_bad_weak_ptr();}

如果弱引用计数中的多态指针_M_pi不为空,则引用计数+1

如果弱引用计数中的多态指针_M_pi为空,则抛出异常,即未引用weak_ptr如果不是生于某shared_ptr,将不能用作新的shared_ptr的创建,而什么时候使用weak_ptr去创建新的shared_ptr呢,怎么创建呢,一会儿分析。

long _M_get_use_count() const noexcept { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0;}bool _M_unique() const noexcept { return this->_M_get_use_count() == 1; }

_Sp_counted_base

_Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) {}virtual ~_Sp_counted_base() noexcept {}virtual void _M_dispose() noexcept = 0;// Called when _M_weak_count drops to zero.virtual void _M_destroy() noexcept { delete this; }

可以看到抽象类_Sp_counted_base的构造函数,初始化了int类型的成员变量:_M_use_count ,_M_weak_count为1,即在原始指针构造shared_ptr时在堆上开辟内存,进行了初始化。

_M_destroy函数,用于销毁自己(实现类**_Sp_counted_ptr),而管理的资源的销毁放在了子类实现(_Sp_counted_ptr**)之中,可以推断,当功能项的子类堆对象析构时,或者析构前,会对管理的资源进行回收。

纯虚接口virtual void _M_dispose() noexcept = 0;在子类中的重写如下:

class _Sp_counted_ptr final : public _Sp_counted_base { public: explicit _Sp_counted_ptr(_Ptr __p) noexcept : _M_ptr(__p) {} virtual void _M_dispose() noexcept { delete _M_ptr; } virtual void _M_destroy() noexcept { delete this; } _Sp_counted_ptr(const _Sp_counted_ptr&) = delete; _Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete; private: _Ptr _M_ptr;};

可以看到实现了_M_dispose(),其将资源真正释放。

而且,重写了_M_destroy(),将自身析构。

接下来就看引用计数加减逻辑和资源释放逻辑。

由于存在多线曾多共享引用计数进行访问,因此对于_M_use_count ,_M_weak_count都要进行原子操作:

void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }void _M_add_ref_lock();bool _M_add_ref_lock_nothrow();void _M_release() noexcept { // Be race-detector-friendly、 For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count); _M_dispose(); // There must be a memory barrier between dispose() and destroy() // to ensure that the effects of dispose() are observed in the // thread that runs destroy(). // See http://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html if (_Mutex_base<_Lp> ::_S_need_barriers) { __atomic_thread_fence(__ATOMIC_ACQ_REL); } // Be race-detector-friendly、 For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); _M_destroy(); } }}void _M_weak_add_ref() noexcept { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1);}void _M_weak_release() noexcept { // Be race-detector-friendly、For more info see bits/c++config. _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count); if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) { _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count); if (_Mutex_base<_Lp> ::_S_need_barriers) { // See _M_release(), // destroy() must observe results of dispose() __atomic_thread_fence(__ATOMIC_ACQ_REL); } _M_destroy(); }}long _M_get_use_count() const noexcept { // No memory barrier is used here so there is no synchronization // with other threads. return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);}

void _M_add_ref_copy() 将引用计数+1

void _M_weak_add_ref()将弱引用计数+1

void _M_weak_release(),首先判断引用计数-1之前,即当前引用计数值是否为1,为1就代表没有别的shared_ptr在引用该资源,调用使用实现类重写的_M_destroy()将自己析构,做到了共享内存不泄露。

void _M_release(),首先判断引用计数-1之前,即当前引用计数值是否为1,为1就代表没有别的shared_ptr在引用该资源,于是调用使用实现类重写的_M_dispose()将资源释放!然后调用使用实现类重写的_M_destroy()将自己析构,做到了共享内存不泄露。

那么什么时候调用_M_weak_release(),什么时候调用_M_release()呢?并且为什么调用_M_weak_release()要去回收资源呢?

1、调用_M_weak_release()

在weak_ptr对weak_ptr进行赋值时,左值将原本的弱引用计数-1时会调用。

在某个weak_ptr析构时,引用计数-1时会调用。

那么,是否存在共享内存中弱引用计数从1减到0的情况呢?答案是不存在:

1、当环境中不存在`weak_ptr`时,弱引用技术保持1。2、当环境中每次从`shared_ptr`中诞生一个`weak_ptr`时,弱引用技术+1,所以最终会递减到1,也不会存在0的情况。

因此weak_ptr不会引起管理的资源的回收。

2、调用_M_release()

在shared_ptr对shared_ptr进行赋值时,左值将原本的弱引用计数-1时会调用。

在某个shared_ptr析构时,引用计数-1时会调用。

由于weak_ptr是由shared_ptr生出,并且也能使用weak_ptr去构造shared_ptr,那么势必能将weak_ptr提升为shared_ptr:

shared_ptr<_Tp> lock() const noexcept { return shared_ptr<_Tp> (*this, std::nothrow);}

这样做会增加引用计数,但是不增加弱引用技术,这样做的好处在于,当想要使用weak_ptr管理的资源时担心资源是否被释放,然后哦按段shared_ptr,如果为真,则资源未释放。

enable_shared_from_this

template class __enable_shared_from_this { protected: constexpr __enable_shared_from_this() noexcept {} __enable_shared_from_this(const __enable_shared_from_this&) noexcept {} __enable_shared_from_this& operator=( const __enable_shared_from_this&) noexcept { return *this; } ~__enable_shared_from_this() {} public: __shared_ptr<_Tp, _Lp> shared_from_this() { return __shared_ptr<_Tp, _Lp> (this->_M_weak_this); } __shared_ptr shared_from_this() const { return __shared_ptr (this->_M_weak_this); } __weak_ptr<_Tp, _Lp> weak_from_this() noexcept { return this->_M_weak_this; } __weak_ptr weak_from_this() const noexcept { return this->_M_weak_this; } private: template void _M_weak_assign(_Tp1* __p, const __shared_count<_Lp> & __n) const noexcept { _M_weak_this._M_assign(__p, __n); } friend const __enable_shared_from_this* __enable_shared_from_this_base( const __shared_count<_Lp> &, const __enable_shared_from_this* __p) { return __p; } template friend class __shared_ptr; mutable __weak_ptr<_Tp, _Lp> _M_weak_this;};

可以看到这个类,含有一个数据成员:_M_weak_this : __weak_ptr<_Tp>

__shared_ptr<_Tp, _Lp> shared_from_this() { return __shared_ptr<_Tp, _Lp> (this->_M_weak_this); }__weak_ptr<_Tp, _Lp> weak_from_this() noexcept { return this->_M_weak_this; }

其中shared_from_this()由内部weak_ptr产生一个shared_ptr,使得共享shared_ptr引用计数+1,那么其内部是如何被初始化的呢?从上面看来是通过调用:

template void _M_weak_assign(_Tp1* __p, const __shared_count<_Lp> & __n) const noexcept { _M_weak_this._M_assign(__p, __n); }

再看一下构造shared_ptr时,入参为enable_shared_frome_this的构造函数:

template >explicit __shared_ptr(_Yp* __p) : _M_ptr(__p), _M_refcount(__p, typename is_array<_Tp> ::type()) { static_assert(!is_void<_Yp> ::value, "incomplete type"); static_assert(sizeof(_Yp) > 0, "incomplete type"); _M_enable_shared_from_this_with(__p);}template ::type>typename enable_if<__has_esft_base<_Yp2>::value>::type_M_enable_shared_from_this_with(_Yp* __p) noexcept { if (auto __base = __enable_shared_from_this_base(_M_refcount, __p)) __base->_M_weak_assign(const_cast<_Yp2*>(__p), _M_refcount);}friend const __enable_shared_from_this* __enable_shared_from_this_base( const __shared_count<_Lp> &, const __enable_shared_from_this* __p) { return __p;}

在初次使用继承了enable_shared_from_this的堆对象取构造shared_ptr时,其内部回调用_M_enable_shared_from_this_with(__p)去接收这个原始指针。
然后在_M_enable_shared_from_this_with(__p)中通过友元函数__enable_shared_from_this_base返回一个enable_shared_from_this的指针,也就是将实际类型指针转型为父类指针。最后调用_M_weak_assign初始化其内weak_ptr。
此时共享的控制块中引用计数为1,弱引用计数也是1+1=2

所以当用户敲下以下代码的时候,就表示当前资源已经被一个weak_ptr引用了

可以推测,一个被shared_ptr管理的资源,通过std::enable_shared_from_this::shared_frome_this()来产生一个共享同一个资源控制快的新shared_ptr,从而保证在第一个shared_ptr声明结束的时候,由于还有shared_ptr管理,因此生命周期被延长了。比如:

struct B { auto asyncFunc(std::function callback) { std::async([=] { sleep(100); callback(); }); }};struct A : std::enable_shared_from_this { B b_; auto callBack() {} auto func() { b_.asyncFunc(std::bind(&A::callBack, shared_frome_this())); }};{ auto a = make_shared(); a->func();}

总结weak_ptr的作用 解决循环引用实现弱回调,不延长对象生命周期,只作为对shared_ptr的观测。回调前判断提升结果,提升成功才做回调。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。