Le permite obtener una shared_ptr
instancia válida para this
, cuando todo lo que tiene es this
. Sin él, no tendría forma de obtener un shared_ptr
a 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_this
se ha convertido en parte del estándar C ++ 11. También puede obtenerlo desde allí, así como desde boost.
std::shared_ptr
constructor en un puntero sin formato si hereda destd::enable_shared_from_this
. No sé si la semántica de Boost se actualizó para respaldar esto.std::shared_ptr
un objeto que ya está administrado por otrostd::shared_ptr
no 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:
Ninguno de los dos
shared_ptr
objetos 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_ptr
objeto que posee el objeto al que se está llamando, no puede crear un objeto sobre la marcha:Este código tiene el mismo problema que el ejemplo anterior, aunque en una forma más sutil. Cuando se construye, el
shared_pt
objeto rsp1
posee el recurso recién asignado. El código dentro de la función miembroS::dangerous
no conoce eseshared_ptr
objeto, por lo que elshared_ptr
objeto que devuelve es distintosp1
. Copiar el nuevoshared_ptr
objeto asp2
no ayuda; cuando estásp2
fuera de alcance, liberará el recurso, y cuandosp1
salga 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:Cuando haga esto, tenga en cuenta que el objeto al que llama
shared_from_this
debe ser propiedad de unshared_ptr
objeto. Esto no funcionará:fuente
shared_ptr<S> sp1(new S);
usarloshared_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 llamarshared_from_this()
la primera vez! ¡Esto es realmente fácil de equivocarse! Antes de C ++ 17, UB debe llamarshared_from_this()
antes de que se haya creado exactamente un shared_ptr de la manera normal:auto sptr = std::make_shared<S>();
oshared_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 llamarsealways_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 unweak_ptr<T>
ejemplo deT
lo que sostiene el ' verdadero contador de referencia ' para la instancia deT
.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 nuevoshared_ptr
básicamente retrocede en estoweak_ptr
.T
puede, entonces, en sus métodos, llamarshared_from_this
para obtener una instancia deshared_ptr<T>
ese respaldo en el mismo recuento de referencia almacenado internamente . De esta manera, siempre tiene un lugar dondeT*
se almacena el recuento de referencias en lugar de tener variasshared_ptr
instancias que no se conocen entre sí, y cada una piensa que son lasshared_ptr
encargadas de contarlasT
y eliminarlas cuando su referencia -cuenta llega a cero.fuente
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 llamarshared_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_this
es frágil para empezar, ya que el punto es poder obtener unshared_ptr<T>
de aT*
, pero en realidad cuando obtienes un punteroT* 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.
fuente
enable_shared_from_this
permite trabajar con una API que acepta específicamenteshared_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
this
como un puntero compartido, ya quethis
le da un puntero sin formato.en otras palabras, te permite activar código como este
dentro de esto:
fuente
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 suNodePtr getParent const()
lugar. Si realmente necesita acceder al puntero sin formato interno (el mejor ejemplo: tratar con una biblioteca C), haystd::shared_ptr<T>::get
algo 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_stub
miembro alclass Y
. Luego escribir:Ú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.
fuente
shared_ptr<>
que no elimine a su puntero, esto es exagerado. Simplemente puede decirreturn shared_ptr<Y>(this, no_op_deleter);
dóndeno_op_deleter
está un objeto de función unaria tomandoY*
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,this
se eliminará y todas las referencias posteriores estarán colgando.enable_shared_from_this
, mantiene una parteweak_ptr
de sí misma (poblada por el ctor), devuelta comoshared_ptr
cuando llamashared_from_this
. En otras palabras, está duplicando lo queenable_shared_from_this
ya proporciona.