Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should we use std::enable_shared_from_this

I just knew std::enable_shared_from_this form this link.
But after reading the code below, I don't know when to use it.

try {
        Good not_so_good;
        std::shared_ptr<Good> gp1 = not_so_good.getptr();
    } catch(std::bad_weak_ptr& e) {
        // undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
        std::cout << e.what() << '\n';    
    }

The code above is "not so good" because there is no existing shared_ptr before calling getptr(). So the good thing should be:

std::shared_ptr<Good> gp1 = std::make_shared<Good>(); // having a shared_ptr at the beginning
std::shared_ptr<Good> gp2 = gp1->getptr();

However, if I have already had a shared_ptr object, why don't I just simply code like this: std::shared_ptr<Good> gp2 = gp1;, meaning that I don't need std::enable_shared_from_this at all.

In my opinion, using std::enable_shared_from_this is to make sure that more than one shared_ptr objects have the same control block so that we can avoid the double-delete problem. But if I must remind myself to create a shared_ptr at the beginning, why don't I just remind myself to use shared_ptr object to create a new one, instead of using raw pointer?

like image 795
Yves Avatar asked Dec 28 '16 15:12

Yves


People also ask

What is the use of Enable_shared_from_this?

std::enable_shared_from_this The class provides functionality that allows objects of derived classes to create instances of shared_ptr pointing to themselves and sharing ownership with existing shared_ptr objects.

Why use shared_ from_ this?

The shared_from_this function searches for the existing control block rather than creating a new one. The simplest way to control this is to make the constructor private. Objects will be created using a static factory function that passes arguments to the private constructor.

How is Enable_shared_from_this implemented?

A common implementation for enable_shared_from_this is to hold a weak reference (such as std::weak_ptr) to this. The constructors of std::shared_ptr detect the presence of an enable_shared_from_this base and assign the newly created std::shared_ptr to the internally stored weak reference.

Can I use Shared_from_this in the constructor?

Second, notice that the weak pointer is set only when the object is placed inside a shared_ptr , which happens after the shared object has been constructed. This means that you cannot use shared_from_this() in your constructor.


3 Answers

The hint about when std::enable_shared_from_this<T> is useful is in its name: when yielding objects based on some requests it may be necessary to return a pointer to an object itself. If the result should be a std::shared_ptr<T> it becomes necessary to return such a pointer from within a member function where there is generally no std::shared_ptr<T> accessible.

Having derived from std::enable_shared_from_this<T> provides a way to get hold of a std::shared_ptr<T> given just a pointer of type T. Doing so does, however, assume that the object is already managed via a std::shared_ptr<T> and it would create mayhem if the object is allocated on the stack:

struct S: std::enable_shared_from_this<S> {
    std::shared_ptr<S> get_object() {
        return this->shared_from_this();
    };
}

int main() {
    std::shared_ptr<S> ptr1 = std::make_shared<S>();
    std::shared_ptr<S> ptr2 = ptr1->get_object();
    // ...
}

In a realistic scenario there is probably some condition under which a std::shared_ptr<T> to the current object is returned.

like image 177
Dietmar Kühl Avatar answered Sep 18 '22 12:09

Dietmar Kühl


There're some use case which you can't use the template std::shared_ptr<T> like opaque pointer.

In that case, it's useful to have this:

In some_file.cpp

struct A : std::enable_shared_from_this<A> {};

extern "C" void f_c(A*);
extern "C" void f_cpp(A* a) {
   std::shared_ptr<A> shared_a = a->shared_from_this();
   // work with operation requires shared_ptr
}

int main()
{
    std::shared_ptr<A> a = std::make_shared<A>();
    f_c(a.get());
}

In some_other.c

struct A;
void f_cpp(struct A* a);
void f_c(struct A* a) {
    f_cpp(a);
}
like image 45
Danh Avatar answered Sep 20 '22 12:09

Danh


Let's say I want to represent a computation tree. We'll have an addition represented as a class deriving from expression with two pointers to expressions, so an expression can be evaluated recursively. However, we need to end the evaluation somewhere, so let's have numbers evaluate to themselves.

class Number;

class Expression : public std::enable_shared_from_this<Expression>
{
public:
    virtual std::shared_ptr<Number> evaluate() = 0;
    virtual ~Expression() {}
};

class Number : public Expression
{
    int x;
public:
    int value() const { return x; }
    std::shared_ptr<Number> evaluate() override
    {
        return std::static_pointer_cast<Number>(shared_from_this());
    }
    Number(int x) : x(x) {}
};

class Addition : public Expression
{
    std::shared_ptr<Expression> left;
    std::shared_ptr<Expression> right;
public:
    std::shared_ptr<Number> evaluate() override
    {
        int l = left->evaluate()->value();
        int r = right->evaluate()->value();
        return std::make_shared<Number>(l + r);
    }
    Addition(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right) :
        left(left),
        right(right)
    {

    }
};

Live on Coliru

Note that the "obvious" way of implementing Number::evaluate() with return std::shared_ptr<Number>(this); is broken because it will result in double delete.

like image 43
milleniumbug Avatar answered Sep 21 '22 12:09

milleniumbug