Home > Net >  How does std::weak_ptr store its "use_count" information?
How does std::weak_ptr store its "use_count" information?

Time:09-27

I tried to understand the principle behind weak_ptr's implementation, especially about ref-counting.

The cppreference https://en.cppreference.com/w/cpp/memory/weak_ptr says weak_ptr works as an observer of shared_ptr, declaring weak_ptr doesn't change the use_count of original shared_ptr. Then my question is, how is weak_ptr implemented to know the use_count of original shared_ptr. I guess weak_ptr will hold a integer pointer to shared_ptr's use_count.

I had a quick test:

using namespace std;
int main()
{
    auto sp = make_shared<int>(10);
    weak_ptr<int> wp(sp);
    cout << wp.use_count() << endl; // 1
    sp.reset(new int{5});
    cout << wp.use_count() << endl; // 0
    auto swp = wp.lock();
    cout << swp.get() << endl; // 0
    cout << *swp << endl;
    return 0;
}

As you could see the result of my program, as comments. So

(1) If weak_ptr doesn't hold an integer pointer to shared_ptr's use_count, then , how does it know the use_count() should change from 1 to 0 when I called sp.reset(new int[5]). How could it know?

(2) If it holds such a pointer, when original shared_ptr came to end of lifecycle and destroyed, this pointer will point to a non-exist position! Dangling pointer!

Thus it seems to me a contradictory about how to implement weak_ptr.

Would you give some hints? Thanks a lot.

CodePudding user response:

I tried to understand the principle behind weak_ptr's implementation

Take one implementation and observe it. For example on libstdc the class __weak_ptr contains https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/shared_ptr_base.h#L1974 the pointer to memory and the counter:

template<typename _Tp, _Lock_policy _Lp>
    class __weak_ptr {
    ...

       _Tp*               _M_ptr;         // Contained pointer.
      __weak_count<_Lp>  _M_refcount;    // Reference counter.
    };

Where __weak_count contains a pointer to the counter:

 template<_Lock_policy _Lp>
    class __weak_count
    {
      ...
      _Sp_counted_base<_Lp>*  _M_pi;
    };

Where _Sp_counted_base actually holds the counters:

  template<_Lock_policy _Lp = __default_lock_policy>
    class _Sp_counted_base
    : public _Mutex_base<_Lp>
    {
      ....
      _Atomic_word  _M_use_count;     // #shared
      _Atomic_word  _M_weak_count;    // #weak   (#shared != 0)
    };

If it holds such a pointer, when original shared_ptr came to end of lifecycle and destroyed, this pointer will point to a non-exist position!

Not if the count itself is dynamically allocated! From https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/shared_ptr_base.h#L913 __shared_count allocates _Sp_counted_ptr on construction dynamically:

template<_Lock_policy _Lp>
class __shared_count
{
   ...
      template<typename _Ptr>
        explicit
    __shared_count(_Ptr __p) : _M_pi(0)
    {
       ...
          _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
       ...
    }
  ...
  _Sp_counted_base<_Lp>*  _M_pi;
};

Where _Sp_counted_ptr is dynamically allocated, and it holds the pointer to memory and the counters by inheriting from _Sp_counted_base:

  template<typename _Ptr, _Lock_policy _Lp>
      // _Sp_counted_base has the count
    class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
    {

    private:
      _Ptr             _M_ptr;  // the pointer to memory
    };
  • Related