¿Cuál es la utilidad de `enable_shared_from_this`?
-
23-08-2019 - |
Pregunta
Me encontré con enable_shared_from_this
durante la lectura de los ejemplos Boost.Asio y después de leer la documentación Todavía estoy perdido de cómo esto correctamente se debe utilizar. Por favor alguien puede darme un ejemplo y / o y explicación de cuando se utiliza esta clase tiene sentido.
Solución
Se le permite obtener una instancia válida para shared_ptr
this
, cuando todo lo que tienes es this
. Sin ella, no tendría ninguna manera de conseguir un shared_ptr
a this
, a menos que ya tenía uno como miembro. Este ejemplo de la documentación impulso rel="noreferrer"> :
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 shared_ptr
válida, a pesar de que no tenía ejemplo miembro. Tenga en cuenta que no se puede simplemente hacer esto:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
El puntero compartida que esta vuelto tendrá un contador de referencia diferente de la "correcta" uno, y uno de ellos va a terminar perdiendo y la celebración de una referencia colgando cuando se elimina el objeto.
enable_shared_from_this
se ha convertido en parte de C ++ 11 estándar. También se puede obtener a partir de ahí, así como de impulso.
Otros consejos
Dr. Dobbs del artículo sobre los punteros débiles, creo que este ejemplo es más fácil de entender (fuente: http: // drdobbs .com / CPP / 184402026 ):
... 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 objetos shared_ptr
sabe acerca de la otra, por lo tanto se trate de liberar el recurso cuando son destruidos. Que por lo general conduce a problemas.
Del mismo modo, si una función miembro necesita un objeto shared_ptr
que posee el objeto que está siendo llamado en adelante, puede no sólo 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 construyó, la shared_pt
objeto sp1
r posee el recurso recién asignada. El código dentro de la función miembro S::dangerous
no sabe nada de ese objeto shared_ptr
, por lo que el objeto shared_ptr
que devuelve es distinta de sp1
. Copiar el nuevo objeto shared_ptr
a sp2
no ayuda; cuando sp2
sale del ámbito, se dará a conocer el recurso, y cuando sp1
sale del ámbito, se dará a conocer el recurso de nuevo.
La forma de evitar este problema es utilizar el enable_shared_from_this
plantilla de clase. La plantilla toma un argumento tipo de plantilla, que es el nombre de la clase que define el recurso gestionado. Esa clase debe, a su vez, puede derivar públicamente a partir de la plantilla; de esta manera:
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;
}
Al hacer esto, tenga en cuenta que el objeto sobre el que se llama a shared_from_this
debe ser propiedad de un objeto shared_ptr
. Esto no funcionará:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Aquí está mi explicación, desde una perspectiva de tuercas y pernos (respuesta no superior 'clic' conmigo). * Tenga en cuenta que este es el resultado de la investigación de la fuente de shared_ptr y enable_shared_from_this que viene con Visual Studio 2012. Tal vez otros compiladores implementan de forma diferente ... * enable_shared_from_this
enable_shared_from_this<T>
agrega una instancia weak_ptr<T>
privado a T
que mantiene el ' una verdadera referencia recuento ' para la instancia de T
.
Por lo tanto, cuando se crea un shared_ptr<T>
en una nueva T *, que weak_ptr interna T * 's se inicializa con un refcount de 1. El nuevo shared_ptr
básicamente se mete hacia esta weak_ptr
.
T
puede entonces, en sus métodos, llamar shared_from_this
para obtener una instancia de shared_ptr<T>
que respalda a la misma referencia almacenado internamente recuento . De esta manera, siempre tendrá un lugar en el ref-recuento de T*
se almacena en lugar de tener varias instancias shared_ptr
que no saben el uno del otro, y cada uno piensa que son la shared_ptr
que está a cargo de T
ref-conteo y elimine cuando su ref-cuenta llega a cero.
Tenga en cuenta que el uso de un impulso :: intrusive_ptr no sufre de este problema. Esto es a menudo una manera más conveniente de conseguir alrededor de este problema.
Es exactamente lo mismo en C ++ 11 y más adelante:. Es para que la capacidad de volver this
como un puntero compartida desde this
le da un puntero prima
en otras palabras, que le permita activar un código como éste
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
en 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;
};
Otra forma es añadir un miembro weak_ptr<Y> m_stub
en el class Y
. A continuación, escriba:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Útil cuando no se puede cambiar la clase que se derivan de (que se extiende por ejemplo, biblioteca de otras personas). No se olvide de inicializar el miembro de, por ejemplo, por m_stub = shared_ptr<Y>(this)
, su es válida incluso durante un constructor.
Está bien si hay más talones como ésta en jerarquía de herencia, no va a evitar la destrucción del objeto.
Editar: Como correctamente señalado por Nobar usuario, el código destruiría objeto Y cuando la asignación está terminado y variables temporales son destruidos. Por lo tanto, mi respuesta es incorrecta.