¿Cuándo es útil std::weak_ptr?

Resuelto asked hace 12 años • 15 respuestas

Comencé a estudiar punteros inteligentes de C++ 11 y no veo ningún uso útil std::weak_ptr. ¿Alguien puede decirme cuándo std::weak_ptres útil/necesario?

 avatar Aug 20 '12 06:08
Aceptado

std::weak_ptres una muy buena manera de resolver el problema del puntero colgante . Con solo usar punteros sin formato, es imposible saber si los datos a los que se hace referencia se han desasignado o no. En cambio, al permitir que un std::shared_ptradministrador administre los datos y los proporcione std::weak_ptra los usuarios, los usuarios pueden verificar la validez de los datos llamando expired()a o lock().

No podría hacer esto std::shared_ptrsolo, porque todas std::shared_ptrlas instancias comparten la propiedad de los datos que no se eliminan antes de que std::shared_ptrse eliminen todas las instancias. Aquí hay un ejemplo de cómo verificar si hay un puntero colgando usando lock():

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << "weak1 value is " << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";
    
    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << "weak2 value is " << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}

Producción

weak1 is expired
weak2 value is 5
sunefred avatar Feb 19 '2014 10:02 sunefred

Un buen ejemplo sería un caché.

Para los objetos a los que se accedió recientemente, desea mantenerlos en la memoria, por lo que mantiene un puntero fuerte hacia ellos. Periódicamente, escanea el caché y decide a qué objetos no se ha accedido recientemente. No es necesario que los guardes en la memoria, por lo que te deshaces del puntero fuerte.

Pero, ¿qué pasa si ese objeto está en uso y algún otro código tiene un fuerte puntero hacia él? Si el caché se deshace de su único puntero al objeto, nunca podrá volver a encontrarlo. Por lo tanto, el caché mantiene un puntero débil a los objetos que necesita encontrar si permanecen en la memoria.

Esto es exactamente lo que hace un puntero débil: le permite localizar un objeto si todavía está presente, pero no lo mantiene si nada más lo necesita.

David Schwartz avatar Aug 19 '2012 23:08 David Schwartz

Otra respuesta, con suerte más sencilla. (para compañeros de Google)

Supongamos que tiene objetos Teamy Member.

Obviamente es una relación: el Teamobjeto tendrá punteros a su Members. Y es probable que los miembros también tengan un puntero hacia atrás a su Teamobjeto.

Entonces tienes un ciclo de dependencia. Si usa shared_ptr, los objetos ya no se liberarán automáticamente cuando abandone la referencia a ellos, porque se hacen referencia entre sí de forma cíclica. Esta es una pérdida de memoria.

Rompes esto usando weak_ptr. El "propietario" normalmente usa shared_ptry el "propiedad" usa a weak_ptrpara su padre, y lo convierte temporalmente cuandoshared_ptr necesita acceso a su padre.

Almacenar un ptr débil:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

luego úsalo cuando sea necesario

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
  // yes, it may fail if the parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope
Offirmo avatar Jul 03 '2013 10:07 Offirmo

Aquí hay un ejemplo, que me dio @jleahy: suponga que tiene una colección de tareas, ejecutadas de forma asincrónica y administradas por un archivo std::shared_ptr<Task>. Es posible que desee hacer algo con esas tareas periódicamente, por lo que un evento de temporizador puede atravesar std::vector<std::weak_ptr<Task>>y dar a las tareas algo que hacer. Sin embargo, simultáneamente una tarea puede haber decidido que ya no es necesaria y morir. De este modo, el temporizador puede comprobar si la tarea sigue activa creando un puntero compartido a partir del puntero débil y utilizando ese puntero compartido, siempre que no sea nulo.

Kerrek SB avatar Aug 19 '2012 23:08 Kerrek SB