问题
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_ptr
s.
回答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