How std::enable_shared_from_this::shared_from_this works

牧云@^-^@ 提交于 2019-12-03 11:40:01

问题


I just cannot understand how std::enable_shared_from_this::shared_from_this returns a shared pinter that shared ownership with existing pointer. In other words you do this:

std::shared_ptr<Foo> getFoo() { return shared_from_this(); }

So when you call getFoo how does exactly it get what is the other shared_ptr to share the ownership with and not to create a separate shared_ptr that owns the same this.

I need to understand this to be able to understand how to create shared_ptr from some object that all increase the same ref count and not initialize separate shared_ptrs.


回答1:


enable_shared_from_this<T> has a weak_ptr<T> data member. The shared_ptr<T> constructor can detect if T is derived from enable_shared_from_this<T>. If it is, the shared_ptr<T> constructor will assign *this (which is the shared_ptr<T>) to the weak_ptr data member in enable_shared_from_this<T>. shared_from_this() can then create a shared_ptr<T> from the weak_ptr<T>.

Example of a possible implementation:

template<class D>
class enable_shared_from_this {
protected:
    constexpr enable_shared_from_this() { }
    enable_shared_from_this(enable_shared_from_this const&) { }
    enable_shared_from_this& operator=(enable_shared_from_this const&) {
        return *this;
    }

public:
    shared_ptr<T> shared_from_this() { return self_.lock(); }
    shared_ptr<T const> shared_from_this() const { return self_.lock(); }

private:
    weak_ptr<D> self_;

    friend shared_ptr<D>;
};

template<typename T>
shared_ptr<T>::shared_ptr(T* ptr) {
    // ...
    // Code that creates control block goes here.
    // ...

    // NOTE: This if check is pseudo-code. Won't compile. There's a few
    // issues not being taken in to account that would make this example
    // rather noisy.
    if (is_base_of<enable_shared_from_this<T>, T>::value) {
        enable_shared_from_this<T>& base = *ptr;
        base.self_ = *this;
    }
}



回答2:


I am submitting an alternative approach in which EnabledSharedFromThis implementation does not add a cost of an extra pointer to class inheriting from it. This is a very crude solution and I am interested in knowing a more efficient solution for implementation of EnabledSharedFromThis.

#include <iostream>
#include <type_traits>
#include <map>
#include <memory>

using namespace std;

template<typename T>
struct EnableShareFromThis;

template<typename T>
struct Shr_ptr;

//SHARED POINTER implementation with lot of flaws :)
template<typename T>
struct Shr_ptr{

  //very brute approach to solving this problem.
  static map<T*, Shr_ptr<T>*> ENABLE_SHARE_STORE;

  protected:
    T* raw;
    int* ref;


  public:

    Shr_ptr():raw(nullptr), ref(nullptr){

      cout << "Shr_ptr...." << endl;

    }

    Shr_ptr(T* p, int* c):raw(p), ref(c){

      ++(*ref);
      cout << "Shr_ptr ctr with raw & ref..." << *c << endl;
    }

    Shr_ptr(T* p):raw(p), ref(new int(1)){

      if(std::is_base_of<EnableShareFromThis<T>, T>::value)
      {
        cout << "   is_base_of<EnableShareFromThis" << endl;

        ENABLE_SHARE_STORE.insert(make_pair(static_cast<T*>(raw), this)); 
      }
      cout << "Shr_ptr...param...ctr..ref=" << *ref << endl;
    }

    bool operator==(const Shr_ptr& rhs){

      return (raw == rhs.raw) && (*ref == *rhs.ref);
    }

    bool operator<(const Shr_ptr& rhs){

      return raw < rhs.raw && *ref < *rhs.ref;
    }

    Shr_ptr& operator=(const Shr_ptr& rhs){

      if(this == &rhs)
        return *this;

      raw = rhs.raw;
      ++(*rhs.ref);
      ref = rhs.ref;
      cout << "Shr_ptr...operator=....ref=" << *ref << endl;

      return *this;
    }

    Shr_ptr(const Shr_ptr& rhs){

      if(rhs.raw)
        raw = rhs.raw;
      if(rhs.ref)
      {
        ++(*rhs.ref);
        ref = rhs.ref;
        cout << "Shr_ptr...cpy ctr...ref=" << *ref <<  endl;
      }
    }

    Shr_ptr(Shr_ptr&& rhs){
      cout << "Shr_ptr...mv ctr...." << endl;
      if(rhs.raw)
      {
        raw = rhs.raw;
        rhs.raw = nullptr;
      }
      if(rhs.ref)
      {
        ++(*rhs.ref);
        ref = rhs.ref;
        cout << "Shr_ptr...mv ctr...ref=" << *ref <<  endl;
        rhs.ref = nullptr;
      }

    }

    T* operator->(){
      return raw;
    }

    T* get() const{return raw;}
    T* get() {return raw;}

    int use_count()
    {
      return *ref;
    }

    ~Shr_ptr()
    {
      cout << "~Shr_ptr ref=" << *ref << endl;
      --*ref;
      if(*ref==0)
      {
        Shr_ptr<T>::ENABLE_SHARE_STORE.erase(raw);
        delete raw;
        raw = nullptr;
      }
    }


};

template<typename T>
    map<T*, Shr_ptr<T>*> Shr_ptr<T>::ENABLE_SHARE_STORE;


template<typename T>
struct EnableShareFromThis{

  EnableShareFromThis()
  {
    cout << "EnableShareFromThis ctr..." << endl;
  }

  Shr_ptr<T> SharedFromThis() 
  {
      auto itr = Shr_ptr<T>::ENABLE_SHARE_STORE.find(static_cast< T*>(this));
      if (itr != Shr_ptr<T>::ENABLE_SHARE_STORE.end())
      {
        cout << "    SharedFromThis called....." << endl;
        return *itr->second;
      }

      return Shr_ptr<T>(static_cast< T*>(this));
  }

};

Class using EnableShareFromThis

 struct A: EnableShareFromThis<A>{

      A(){cout << "A ctr....m" << endl;}
      ~A(){cout << "A dtr....m" << endl;}

      Shr_ptr<A> f()
      {
        return  SharedFromThis();
      }
    };

Class without EnableShareFromThis

struct B{

  B(){cout << "B ctr...." << endl;}
  ~B(){cout << "B dtr...." << endl;}
};

Test case

 void test()
    {
      //Check size of different objects, std enabled_from_this does not add extra size to A
      cout << "Size of A=" << sizeof(A) << " size of Shr_ptr=" << sizeof(Shr_ptr<A>) << " std::shr_ptr size=" << sizeof(shared_ptr<A>) << endl;

      Shr_ptr<A> p1(new A());
      cout << endl;

      cout << "{" << endl;
      {
        //Get SharedFromThis
        Shr_ptr<A> p5 = p1->f();
        Shr_ptr<A> p6 = p5->f();
        cout << "p5 use_count=" << p5.use_count() << ", p1 use_count=" << p1.use_count() << endl;
      }
      cout << "}" << endl;
      cout << endl;

      cout << "{" << endl;
      { 
        //Without SharedFromThis
        Shr_ptr<B> p7(new B());
        cout << "p7 use_count=" << p7.use_count() <<  endl;
      }
      cout << "}" << endl;
      cout << endl;

    }

    int main()
    {
      test();
      return 0;
    }


来源:https://stackoverflow.com/questions/34061515/how-stdenable-shared-from-thisshared-from-this-works

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!