unique_ptr smart punteros inteligentes c++ c++11 shared-ptr weak-ptr cyclic-reference

c++ - smart - diferencias shared_ptr y weak_ptr



smart pointers c++ (5)

Los indicadores débiles simplemente "observan" el objeto gestionado; no lo "mantienen vivo" ni afectan su duración. A diferencia de shared_ptr , cuando el último weak_ptr sale del alcance o desaparece, el objeto apuntado puede seguir existiendo porque el weak_ptr no afecta la vida útil del objeto, no tiene derechos de propiedad. El weak_ptr se puede usar para determinar si el objeto existe y para proporcionar un shared_ptr que se puede usar para referirse a él.

La definición de weak_ptr está diseñada para que sea relativamente infalible, por lo que es muy poco lo que se puede hacer directamente con un weak_ptr . Por ejemplo, no puedes desreferenciarlo; ni operator* ni operator-> se define para un weak_ptr . No puede acceder al puntero al objeto con él; no hay función get() . Hay una función de comparación definida para que pueda almacenar weak_ptrs en un contenedor ordenado, pero eso es todo.

Estoy leyendo el libro de Scott Meyers "Effective C ++". Se mencionó que hay tr1::shared_ptr y tr1::weak_ptr funcionan como punteros incorporados, pero hacen un seguimiento de cuántos tr1::shared_ptrs apuntan a un objeto.

Esto se conoce como recuento de referencia. Esto funciona bien para evitar fugas de recursos en estructuras de datos acíclicas, pero si dos o más objetos contienen tr1::shared_ptrs modo que se forme un ciclo, el ciclo puede mantener el recuento de referencias de cada uno por encima de cero, incluso cuando todos los punteros externos al ciclo tienen sido destruido

Ahí es donde tr1::weak_ptrs .

Mi pregunta es cómo las estructuras cíclicas de datos hacen que la referencia cuente por encima de cero. Acepto amablemente un ejemplo de programa C ++. ¿Cómo se soluciona el problema con weak_ptrs ? (de nuevo, con el ejemplo, por favor).


Para futuros lectores.
Solo quiero señalar que la explicación dada por Atom es excelente, aquí está el código de trabajo

#include <memory> // and others using namespace std; class B; // forward declaration // for clarity, add explicit destructor to see that they are not called class A { public: shared_ptr<B> b; ~A() {cout << "~A()" << endl; } }; class B { public: shared_ptr<A> a; ~B() {cout << "~B()" << endl; } }; shared_ptr<A> x(new A); //x->b share_ptr is default initialized x->b = make_shared<B>(); // you can''t do "= new B" on shared_ptr x->b->a = x; cout << x.use_count() << endl;


Permítame repetir su pregunta: "Mi pregunta, cómo las estructuras cíclicas de datos hacen que la referencia cuente por encima de cero, solicite amablemente que se muestre con el ejemplo en el programa C ++. Cómo se soluciona el problema mediante weak_ptrs nuevamente, por ejemplo, por favor".

El problema ocurre con un código de C ++ como este (conceptualmente):

class A { shared_ptr<B> b; ... }; class B { shared_ptr<A> a; ... }; shared_ptr<A> x(new A); // +1 x->b = new B; // +1 x->b->a = x; // +1 // Ref count of ''x'' is 2. // Ref count of ''x->b'' is 1. // When ''x'' leaves the scope, there will be a memory leak: // 2 is decremented to 1, and so both ref counts will be 1. // (Memory is deallocated only when ref count drops to 0)

Para responder la segunda parte de su pregunta: matemáticamente es imposible que el recuento de referencias trate de ciclos. Por lo tanto, un weak_ptr (que básicamente es una versión shared_ptr de shared_ptr ) no se puede usar para resolver el problema del ciclo: el programador está resolviendo el problema del ciclo.

Para resolverlo, el programador debe conocer la relación de propiedad entre los objetos, o necesita inventar una relación de propiedad si no existe tal propiedad de forma natural.

El código C ++ anterior se puede cambiar para que A tenga B:

class A { shared_ptr<B> b; ... }; class B { weak_ptr<A> a; ... }; shared_ptr<A> x(new A); // +1 x->b = new B; // +1 x->b->a = x; // No +1 here // Ref count of ''x'' is 1. // Ref count of ''x->b'' is 1. // When ''x'' leaves the scope, its ref count will drop to 0. // While destroying it, ref count of ''x->b'' will drop to 0. // So both A and B will be deallocated.

Una pregunta crucial es: ¿ weak_ptr puede usar weak_ptr en caso de que el programador no pueda distinguir la relación de propiedad y no pueda establecer ninguna propiedad estática debido a la falta de privilegios o la falta de información?

La respuesta es: si la propiedad entre los objetos no está clara, weak_ptr no puede ayudar. Si hay un ciclo, el programador tiene que encontrarlo y romperlo. Un remedio alternativo es usar un lenguaje de programación con recolección completa de basura (como: Java, C #, Go, Haskell), o usar un recolector de basura conservador (= imperfecto) que funcione con C / C ++ (como: Boehm GC) .


Todas las respuestas anteriores son INCORRECTAS. weak_ptr NO se usa para romper referencias cíclicas, tienen otro propósito.

Básicamente, si todas las shared_ptr(s) fueron creadas por make_shared() o allocate_shared() llamadas, NUNCA necesitarás weak_ptr si no tienes otro recurso que memoria para administrar. Estas funciones crean el objeto del contador de referencia shared_ptr con el objeto en sí, y la memoria se liberará al mismo tiempo.

La única diferencia entre weak_ptr y shared_ptr es que el weak_ptr permite que el objeto del contador de referencia se guarde después de liberar el objeto real. Como resultado, si mantiene una gran cantidad de shared_ptr en un std::set los objetos reales ocuparán mucha memoria si son lo suficientemente grandes. Este problema se puede resolver utilizando weak_ptr en weak_ptr lugar. En este caso, debe asegurarse de que weak_ptr almacenado en el contenedor no haya expirado antes de usarlo.


Un shared_ptr envuelve un mecanismo de conteo de referencia alrededor de un puntero sin formato. Por lo tanto, para cada instancia de shared_ptr el recuento de referencias se incrementa en uno. Si dos objetos share_ptr se refieren entre sí, nunca serán eliminados porque nunca terminarán con un recuento de referencias de cero.

weak_ptr apunta a un shared_ptr pero no aumenta su recuento de referencia. Esto significa que el objeto subyacente aún se puede eliminar aunque haya una referencia de weak_ptr al mismo.

La forma en que esto funciona es que se puede usar el weak_ptr para crear un shared_ptr para cuando se quiera usar el objeto subyacente. Sin embargo, si el objeto ya ha sido eliminado, se devuelve una instancia vacía de un shared_ptr . Dado que el recuento de referencias en el objeto subyacente no se incrementa con una referencia weak_ptr , una referencia circular no dará como resultado que el objeto subyacente no se elimine.