Me encontré con enable_shared_from_this
mientras leía los ejemplos de Boost.Asio y después de leer la documentación todavía estoy perdido por cómo debería usarse correctamente. ¿Puede alguien darme un ejemplo y/o una explicación de cuando usar esta clase tiene sentido?¿Cuál es la utilidad de `enable_shared_from_this`?
Respuesta
Le permite obtener una instancia shared_ptr
válida a this
, cuando todo lo que tiene es this
. Sin él, no habría manera de obtener un shared_ptr
a this
, a menos que ya tenga uno como miembro. Este ejemplo de la boost documentation for 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 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 el objeto esta borrado.
enable_shared_from_this
va a formar parte del nuevo estándar C++ 0x también, por lo que también puede obtenerlo desde allí o desde el impulso.
Otra forma es agregar un miembro weak_ptr<Y> m_stub
en class Y
. A continuación, escriba:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
útil cuando no se puede cambiar la clase va a derivar a partir de (por ejemplo, extender la biblioteca de otras personas). No olvide inicializar al miembro, p. por m_stub = shared_ptr<Y>(this)
, es válido incluso durante un constructor.
Está bien si hay más trozos como este en la jerarquía de herencia, no impedirá la destrucción del objeto.
Editar: Como señala correctamente el usuario nobar, el código destruiría el objeto Y cuando se termine la asignación y se destruyan las variables temporales. Por lo tanto, mi respuesta es incorrecta.
Si su intención aquí es producir un 'shared_ptr <>' que no elimine su punta, esto es overkill.Simplemente puede decir 'return shared_ptr
Parece poco probable que esta sea una solución funcional. 'm_stub = shared_ptr
El autor reconoce que esta respuesta es incorrecta, por lo que probablemente podría simplemente eliminarla. Pero la última vez que se conectó fue de 4.5 años, por lo que no es probable que lo haga. ¿Podría alguien con poderes superiores eliminar esta red arenque? –
del artículo Dr. Dobbs 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);
Ni de los dos objetos shared_ptr
sabe sobre el otro, por lo que ambos intentarán liberar el recurso cuando se destruyan. Eso usualmente lleva 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 construye, el objeto shared_pt
r sp1
posee el recurso recientemente asignado. El código dentro de la función de miembro S::dangerous
no conoce el objeto shared_ptr
, por lo que el objeto shared_ptr
que devuelve es distinto de sp1
.Copiar el nuevo objeto shared_ptr
al sp2
no ayuda; cuando sp2
sale del alcance, liberará el recurso, y cuando sp1
quede fuera del alcance, liberará el recurso nuevamente.
La forma de evitar este problema es utilizar 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; 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
}
Gracias, esto ilustra que el problema se resuelve mejor que la respuesta actualmente aceptada. – goertzenator
+1: Buena respuesta. Como un aparte, en lugar de 'shared_ptr sp1 (new S);' se puede preferir usar 'shared_ptr sp1 = make_shared ();', ver por ejemplo http://stackoverflow.com/questions/18301511/stdshared -ptr-initialization-make-sharedfoo-vs-shared-ptrtnew-foo –
Arun
Estoy bastante seguro de que la última línea debería leer 'shared_ptr sp2 = p-> not_dangerous();' porque la trampa aquí es que debes ** crea un shared_ptr de la manera normal antes de llamar a 'shared_from_this()' la primera vez! ** ¡Es muy fácil equivocarse! Antes de C++ 17 es ** UB ** llamar a 'shared_from_this()' antes de que se haya creado exactamente un shared_ptr de la manera normal: 'auto sptr = std :: make_shared ();' o 'shared_ptr sptr (new S()); '. Afortunadamente desde C++ 17 en adelante lo hará tirar. –
AnorZaken
Tenga en cuenta que el uso de boost :: intrusive_ptr no presenta este problema. Esta es a menudo una forma más conveniente de evitar este problema.
Sí, pero 'enable_shared_from_this' le permite trabajar con una API que acepta específicamente' shared_ptr <> '. En mi opinión, tal API suele ser * Doing It Wrong * (ya que es mejor dejar que algo más alto en la pila sea de la memoria) pero si se ve obligado a trabajar con una API así, esta es una buena opción. – cdunn2001
Aquí está mi explicación, desde una perspectiva de tuercas y tornillos (la respuesta superior no hizo '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 enable_shared_from_this diferente ... *
enable_shared_from_this<T>
añade un weak_ptr<T>
instancia privada a T
que mantiene el 'uno verdadero recuento de referencia '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
respalda básicamente en este weak_ptr
.
T
puede entonces, en sus métodos, llamar shared_from_this
para obtener una instancia de shared_ptr<T>
que mete hacia el mismo número de referencia almacenada internamente. De esta manera, siempre tiene un lugar donde se almacena el ref-count de T*
en lugar de tener múltiples instancias de shared_ptr
que no se conocen entre sí, y cada una cree que son shared_ptr
que se encargan del conteo de ref T
y borran cuando su recuento de ref llega a cero.
Esto es correcto, y la parte realmente importante es 'Entonces, cuando crea ...' porque es un ** requisito ** (como usted dice, el weak_ptr no se inicializa hasta que pase el puntero de los objetos a un shared_ptr ctor!) y este requisito es donde las cosas pueden salir terriblemente mal si no tienes cuidado. Si no creas ningún shared_ptr antes de llamar a 'shared_from_this' obtienes UB, del mismo modo si creas más de un shared_ptr también obtienes UB. De alguna manera, debe asegurarse de crear una shared_ptr _exactly_ una vez. – AnorZaken
En otras palabras, la idea de 'enable_shared_from_this' es frágil, ya que el punto es ser capaz de obtener' shared_ptr
"_internal weak_ptr se inicializa con un refcount de 1_" weak ptr to T no posee ptr inteligente a T. Un ptr débil es un ref inteligente poseedor de suficiente información para hacer un ptr propietario que es una "copia" de otro ptr propietario . Un ptr débil no tiene recuento de ref. Tiene acceso a un recuento de ref, como todos los propietarios de ref. – curiousguy
Es exactamente lo mismo en C++ 11 y posterior: es para habilitar la capacidad de devolver this
como un puntero compartido ya que this
le da un puntero sin formato.
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;
};
Esto solo funcionará si estos objetos siempre son administrados por un 'shared_ptr'. Es posible que desee cambiar la interfaz para asegurarse de que sea el caso. – curiousguy
Tiene toda la razón @curiousguy. Esto es evidente.También me gusta escribir todo mi shared_ptr para mejorar la legibilidad al definir mis API públicas. Por ejemplo, en lugar de 'std :: shared_ptr
- 1. ¿Cuál es la utilidad de los tipos NMTOKEN y NMTOKENS?
- 2. ¿Cuál es la utilidad del tipo de matriz?
- 3. ¿Cuál es la utilidad del atributo GeneratedCodeAttribute en C#?
- 4. Clase de utilidad: ¿Cuál es el enfoque correcto?
- 5. enable_shared_from_this y objetos en la pila
- 6. ¿Cuál es la utilidad real de la marca y la hormiga?
- 7. ¿Cuál es la forma correcta de agregar un módulo de utilidad a una distribución de CPAN?
- 8. Cómo enable_shared_from_this de ambos padres y derivado
- 9. ¿Cuál es la utilidad de los constructores públicos en clases abstractas en C#?
- 10. ¿Cuál es la utilidad de los bloques CHECK, UNITCHECK e INIT en Perl?
- 11. enable_shared_from_this (C++ 0x): ¿qué estoy haciendo mal?
- 12. Utilidad de la señalización NaN?
- 13. Utilidad de JNI
- 14. error de tipo incompleta sobre el uso de impulso :: enable_shared_from_this
- 15. ¿Qué es 'borrar bloque' en la utilidad `mkfs.jffs2 'en Linux?
- 16. Utilidad del método 'java.lang.reflect.Method.getDefaultValue()'?
- 17. iOS: utilidad de didReceiveMemoryWarning:
- 18. Utilidad de System.Diagnostics.Contracts en la pregunta
- 19. Utilidad de const (C++)
- 20. Utilidad de parche gráfica
- 21. ¿Por qué enable_shared_from_this tiene un destructor no virtual?
- 22. Organización de funciones de utilidad en C++
- 23. ¿Es malo tener un archivo grande de utilidad?
- 24. ¿Cuál es la mejor herramienta de compresión/ofuscación de JavaScript?
- 25. ¿Qué es la utilidad de Linux para modificar un nombre de símbolo de C++?
- 26. utilidad para generar xsd de la clase java
- 27. ¿Dónde puedo obtener la utilidad cabarc?
- 28. Alcance de la utilidad de la interfaz en java
- 29. Utilidad de comparación de archivos
- 30. ¿Cuál es la definición de objeto de servicio?
+1. El punto clave es que la técnica "obvia" de simplemente devolver shared_ptr (this) está rota, porque esto termina creando múltiples objetos shared_ptr distintos con recuentos de referencia separados. Por este motivo, nunca debe crear más de un shared_ptr ** del mismo puntero sin formato **. –
Se debe tener en cuenta que en _C++ 11 y posterior_, es ** perfectamente válido ** usar un constructor 'std :: shared_ptr' en un _raw pointer_ ** si ** hereda de' std :: enable_shared_from_this' . ** No sé si ** la semántica de Boost se actualizó para apoyar esto. –