c++ - library - ¿Cuál es la utilidad de `enable_shared_from_this`?
boost library c++ download (6)
Aquí está mi explicación, desde la perspectiva de las tuercas y los tornillos (la respuesta principal no "hizo clic" conmigo). * Tenga en cuenta que este es el resultado de investigar la fuente de shared_ptr y enable_shared_from_this que viene con Visual Studio 2012. Tal vez otros compiladores implementen enable_shared_from_this diferentemente ... *
enable_shared_from_this<T>
agrega una weak_ptr<T>
privada weak_ptr<T>
a T
que contiene el '' único conteo de referencias verdaderas '' para la instancia de T
Entonces, cuando creas un shared_ptr<T>
en un nuevo T *, el valor de weak_ptr interno de T * se inicializa con un refcount de 1. El nuevo shared_ptr
básicamente retrocede en este weak_ptr
.
Luego, T
puede, en sus métodos, llamar a shared_from_this
para obtener una instancia de shared_ptr<T>
que retrocede en el mismo recuento de referencia almacenado internamente . De esta manera, siempre tiene un lugar donde se almacena el recuento de ref de T*
lugar de tener múltiples instancias de shared_ptr
que no se conocen entre sí, y cada uno piensa que es el shared_ptr
que está a cargo del recuento de ref. T
y eliminándolo cuando su recuento de ref llega a cero.
Me encontré con enable_shared_from_this
mientras leía los ejemplos de Boost.Asio y, después de leer la documentación, aún no entiendo cómo debería usarse correctamente. ¿Puede alguien darme un ejemplo y / o una explicación de cuándo usar esta clase tiene sentido?
Es exactamente lo mismo en c ++ 11 y posteriores: es para habilitar la capacidad de devolver this
como un puntero compartido, ya que this
le da un puntero en bruto.
en otras palabras, te permite convertir 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;
};
Le permite obtener una instancia de shared_ptr
válida para this
, cuando todo lo que tiene es this
. Sin él, no tendría forma de obtener un shared_ptr
para this
, a menos que ya tuviera uno como miembro. Este ejemplo de la documentación de boost 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 shared_ptr
válido, aunque 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 devolvió tendrá un recuento de referencia diferente del "correcto", y uno de ellos terminará perdiendo y manteniendo una referencia colgante cuando se elimina el objeto.
enable_shared_from_this
ha convertido en parte del estándar C ++ 11. También puede obtenerlo desde allí, así como de impulso.
Otra forma es agregar un weak_ptr<Y> m_stub
en la class Y
A continuación, escribir:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Es útil cuando no puede cambiar la clase de la que se deriva (por ejemplo, ampliar la biblioteca de otras personas). No olvide inicializar el miembro, por ejemplo, por m_stub = shared_ptr<Y>(this)
, es válido incluso durante un constructor.
Está bien si hay más apéndices como este en la jerarquía de herencia, no evitará la destrucción del objeto.
Edición: Como lo señaló correctamente el usuario nobar, el código destruiría el objeto Y cuando la tarea finalice y las variables temporales se destruyan. Por lo tanto mi respuesta es incorrecta.
Tenga en cuenta que el uso de boost :: intrusive_ptr no sufre este problema. Esta es a menudo una forma más conveniente de solucionar este problema.
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 ):
... 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
conoce el otro, por lo que ambos intentarán liberar el recurso cuando se destruyan. Eso suele llevar a problemas.
De manera similar, si una función miembro necesita un objeto shared_ptr
que posee el objeto al que se llama, no puede simplemente 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 objeto shared_pt
r sp1
posee el recurso recién asignado. El código dentro de la función miembro S::dangerous
no conoce ese objeto shared_ptr
, por lo que el objeto shared_ptr
que devuelve es distinto de sp1
. Copiar el nuevo objeto shared_ptr
a sp2
no ayuda; cuando sp2
queda fuera del alcance, liberará el recurso, y cuando sp1
salga del alcance, liberará el recurso nuevamente.
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 sobre el que llama a shared_from_this
debe ser propiedad de shared_ptr
object. Esto no funcionará:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don''t do this
}