Acabo de conocer std::enable_shared_from_this forma este enlace.
Pero después de leer el código a continuación, no sé cuándo usarlo.

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';    
    }

El código anterior "no es tan bueno" porque no hay shared_ptr antes de llamar a getptr(). Entonces lo bueno debería ser:

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

Sin embargo, si ya he tenido un objeto shared_ptr, ¿por qué no simplemente codifico así: std::shared_ptr<Good> gp2 = gp1;, lo que significa que no necesito std::enable_shared_from_this en absoluto.

En mi opinión, usar std::enable_shared_from_this es para asegurarnos de que más de un shared_ptr objeto tenga el mismo bloque de control para que podamos evitar el problema de doble eliminación. Pero si debo recordarme crear un shared_ptr al principio, ¿por qué no me recuerdo a mí mismo usar el objeto shared_ptr para crear uno nuevo, en lugar de usar un puntero sin formato?

13
Yves 28 dic. 2016 a las 18:08

3 respuestas

La mejor respuesta

La sugerencia sobre cuándo std::enable_shared_from_this<T> es útil está en su nombre: cuando se obtienen objetos basados en algunas solicitudes, puede ser necesario devolver un puntero a un objeto en sí mismo. Si el resultado fuera un std::shared_ptr<T>, es necesario devolver dicho puntero desde una función miembro donde generalmente no hay std::shared_ptr<T> accesible.

El haber derivado de std::enable_shared_from_this<T> proporciona una forma de obtener un std::shared_ptr<T> dado solo un puntero de tipo T. Sin embargo, al hacerlo, se supone que el objeto ya está administrado a través de un std::shared_ptr<T> y crearía un caos si el objeto se asigna en la pila:

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();
    // ...
}

En un escenario realista, probablemente haya alguna condición bajo la cual se devuelva un std::shared_ptr<T> al objeto actual.

7
dfri 1 mar. 2017 a las 16:18

Hay algunos casos de uso que no puede utilizar la plantilla std::shared_ptr<T> como puntero opaco.

En ese caso, es útil tener esto:

En 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());
}

En some_other.c

struct A;
void f_cpp(struct A* a);
void f_c(struct A* a) {
    f_cpp(a);
}
2
Danh 28 dic. 2016 a las 15:50

Digamos que quiero representar un árbol de cálculo. Tendremos una suma representada como una clase derivada de la expresión con dos punteros a expresiones, por lo que una expresión puede evaluarse de forma recursiva. Sin embargo, necesitamos finalizar la evaluación en algún lugar, así que hagamos que los números se evalúen a sí mismos.

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

Tenga en cuenta que la forma "obvia" de implementar Number::evaluate() con return std::shared_ptr<Number>(this); está rota porque dará como resultado una doble eliminación.

2
milleniumbug 31 dic. 2016 a las 04:06