栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++2.0 shared

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++2.0 shared

在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的观测。回调前判断提升结果,提升成功才做回调。
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号