Respuestas:
Le permite obtener una shared_ptrinstancia válida para this, cuando todo lo que tiene es this. Sin él, no tendría forma de obtener un shared_ptra this, a menos que ya tuviera uno como miembro. Este ejemplo de la documentación de impulso para enable_shared_from_this :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
El método f()devuelve un válido shared_ptr, a pesar de que no tenía una instancia miembro. Tenga en cuenta que no puede simplemente hacer esto:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
El puntero compartido que este devuelto tendrá un recuento de referencia diferente del "correcto", y uno de ellos terminará perdiendo y manteniendo una referencia colgante cuando se elimine el objeto.
enable_shared_from_thisse ha convertido en parte del estándar C ++ 11. También puede obtenerlo desde allí, así como desde boost.
std::shared_ptrconstructor en un puntero sin formato si hereda de std::enable_shared_from_this. No sé si la semántica de Boost se actualizó para respaldar esto.
std::shared_ptrun objeto que ya está administrado por otro std::shared_ptrno consultará la referencia débil almacenada internamente y, por lo tanto, conducirá a un comportamiento indefinido". ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )
shared_ptr<Y> q = p?
std::make_shared<T>.
del artículo del Dr. Dobbs sobre punteros débiles, creo que este ejemplo es más fácil de entender (fuente: http://drdobbs.com/cpp/184402026 ):
... un código como este no funcionará correctamente:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Ninguno de los dos shared_ptrobjetos conoce al otro, por lo que ambos intentarán liberar el recurso cuando sean destruidos. Eso generalmente lleva a problemas.
Del mismo modo, si una función miembro necesita un shared_ptrobjeto que posee el objeto al que se está llamando, no puede crear un objeto sobre la marcha:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
Este código tiene el mismo problema que el ejemplo anterior, aunque en una forma más sutil. Cuando se construye, el shared_ptobjeto r sp1posee el recurso recién asignado. El código dentro de la función miembro S::dangerousno conoce ese shared_ptrobjeto, por lo que el shared_ptrobjeto que devuelve es distinto sp1. Copiar el nuevo shared_ptrobjeto a sp2no ayuda; cuando está sp2fuera de alcance, liberará el recurso, y cuando sp1salga del alcance, volverá a liberar el recurso.
La forma de evitar este problema es usar la plantilla de clase enable_shared_from_this. La plantilla toma un argumento de tipo de plantilla, que es el nombre de la clase que define el recurso administrado. Esa clase debe, a su vez, derivarse públicamente de la plantilla; Me gusta esto:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
Cuando haga esto, tenga en cuenta que el objeto al que llama shared_from_thisdebe ser propiedad de un shared_ptrobjeto. Esto no funcionará:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
shared_ptr<S> sp1(new S);usarlo shared_ptr<S> sp1 = make_shared<S>();, puede preferir usar stackoverflow.com/questions/18301511/…
shared_ptr<S> sp2 = p->not_dangerous();porque el problema aquí es que debes crear un shared_ptr de la manera normal antes de llamar shared_from_this()la primera vez! ¡Esto es realmente fácil de equivocarse! Antes de C ++ 17, UB debe llamar shared_from_this()antes de que se haya creado exactamente un shared_ptr de la manera normal: auto sptr = std::make_shared<S>();o shared_ptr<S> sptr(new S());. Afortunadamente, desde C ++ 17 en adelante, arrojará.
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();<- Se permite llamar a shared_from_this solo en un objeto previamente compartido, es decir, en un objeto administrado por std :: shared_ptr <T>. De lo contrario, el comportamiento es indefinido (hasta C ++ 17) std :: bad_weak_ptr es arrojado (por el constructor shared_ptr desde un punto débil construido por defecto) (desde C ++ 17). . Entonces, la realidad es que debería llamarse always_dangerous(), porque necesita saber si ya se ha compartido o no.
Aquí está mi explicación, desde una perspectiva de tuercas y pernos (la respuesta principal no 'hizo clic' conmigo). * Tenga en cuenta que este es el resultado de investigar el origen de shared_ptr y enable_shared_from_this que viene con Visual Studio 2012. Quizás otros compiladores implementen enable_shared_from_this de manera diferente ... *
enable_shared_from_this<T>privada añade un weak_ptr<T>ejemplo de Tlo que sostiene el ' verdadero contador de referencia ' para la instancia de T.
Entonces, cuando creas un shared_ptr<T>nuevo en un nuevo T *, el débil_ptr interno de ese T * se inicializa con un recuento de 1. El nuevo shared_ptrbásicamente retrocede en esto weak_ptr.
Tpuede, entonces, en sus métodos, llamar shared_from_thispara obtener una instancia de shared_ptr<T>ese respaldo en el mismo recuento de referencia almacenado internamente . De esta manera, siempre tiene un lugar donde T*se almacena el recuento de referencias en lugar de tener varias shared_ptrinstancias que no se conocen entre sí, y cada una piensa que son las shared_ptrencargadas de contarlas Ty eliminarlas cuando su referencia -cuenta llega a cero.
So, when you first create...porque es un requisito (ya que usted dice que el débil_ptr no se inicializa hasta que pasa el puntero de los objetos a un controlador_compartido) y este requisito es donde las cosas pueden salir terriblemente mal si está no es cuidadoso. Si no crea shared_ptr antes de llamar shared_from_this, obtendrá UB; del mismo modo, si crea más de shared_ptr, también obtendrá UB. Tiene que asegurarse de alguna manera de crear un shared_ptr exactamente una vez.
enable_shared_from_thises frágil para empezar, ya que el punto es poder obtener un shared_ptr<T>de a T*, pero en realidad cuando obtienes un puntero T* t, generalmente no es seguro asumir nada acerca de que ya se ha compartido o no, y hacer una suposición equivocada es UB.
Tenga en cuenta que el uso de boost :: intrusive_ptr no sufre este problema. Esta suele ser una forma más conveniente de solucionar este problema.
enable_shared_from_thispermite trabajar con una API que acepta específicamente shared_ptr<>. En mi opinión, dicha API suele estar haciendo mal (ya que es mejor dejar que algo más alto en la pila posea la memoria), pero si se ve obligado a trabajar con una API de este tipo, esta es una buena opción.
Es exactamente lo mismo en c ++ 11 y versiones posteriores: es para permitir la posibilidad de regresar thiscomo un puntero compartido, ya que thisle da un puntero sin formato.
en otras palabras, te permite activar código como este
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
dentro de esto:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
shared_ptr. Es posible que desee cambiar la interfaz para asegurarse de que sea así.
std::shared_ptr<Node> getParent const(), normalmente lo expondría como en su NodePtr getParent const()lugar. Si realmente necesita acceder al puntero sin formato interno (el mejor ejemplo: tratar con una biblioteca C), hay std::shared_ptr<T>::getalgo que odio mencionar porque he usado este descriptor de acceso sin formato demasiadas veces por la razón equivocada.
Otra forma es agregar un weak_ptr<Y> m_stubmiembro al class Y. Luego escribir:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Útil cuando no puede cambiar la clase de la que deriva (por ejemplo, ampliar la biblioteca de otras personas). No olvide inicializar el miembro, por ejemplo m_stub = shared_ptr<Y>(this), por , es válido incluso durante un constructor.
Está bien si hay más trozos como este en la jerarquía de herencia, no evitará la destrucción del objeto.
Editar: como lo señaló correctamente el usuario nobar, el código destruiría el objeto Y cuando finalice la asignación y se destruyan las variables temporales. Por lo tanto mi respuesta es incorrecta.
shared_ptr<>que no elimine a su puntero, esto es exagerado. Simplemente puede decir return shared_ptr<Y>(this, no_op_deleter);dónde no_op_deleterestá un objeto de función unaria tomando Y*y sin hacer nada.
m_stub = shared_ptr<Y>(this)construirá e inmediatamente destruirá un shared_ptr temporal a partir de esto. Cuando termine esta declaración, thisse eliminará y todas las referencias posteriores estarán colgando.
enable_shared_from_this, mantiene una parte weak_ptrde sí misma (poblada por el ctor), devuelta como shared_ptrcuando llama shared_from_this. En otras palabras, está duplicando lo que enable_shared_from_thisya proporciona.