Compartir a través de


Procedimiento Creación y uso de instancias weak_ptr

A veces, un objeto debe almacenar una manera de tener acceso al objeto subyacente de una instancia de shared_ptr sin hacer que aumente el recuento de referencias. Normalmente, esta situación se produce cuando se tienen referencias cíclicas entre instancias de shared_ptr.

El mejor diseño es evitar la propiedad compartida de punteros siempre que sea posible. Sin embargo, si debe tener la propiedad compartida de las instancias de shared_ptr, evite las referencias cíclicas entre ellas. Cuando las referencias cíclicas sean inevitables o incluso preferibles por alguna razón, use weak_ptr para dar a uno o varios de los propietarios una referencia débil a otra instancia de shared_ptr. Mediante una instancia de weak_ptr, puede crear una instancia de shared_ptr que se una a un conjunto existente de instancias relacionadas, pero solo si el recurso de memoria subyacente sigue siendo válido. Una instancia de weak_ptr propiamente dicha no participa en el recuento de referencias y, por lo tanto, no puede impedir que el recuento de referencias vaya a cero. Sin embargo, puede usar una instancia de weak_ptr para intentar obtener una nueva copia de shared_ptr con la que se inicializó. Si ya se ha eliminado la memoria, el operador bool de weak_ptr devuelve false. Si la memoria sigue siendo válida, el nuevo puntero compartido incrementa el recuento de referencias y garantiza que la memoria será válida siempre y cuando la variable shared_ptr permanezca en el ámbito.

Ejemplo

En el ejemplo de código siguiente se muestra un caso donde se usa weak_ptr para garantizar la eliminación adecuada de los objetos que tienen dependencias circulares. Cuando examine el ejemplo, suponga que se creó solo después de que se consideraran soluciones alternativas. Los objetos Controller representan algún aspecto de un proceso de máquina y funcionan de forma independiente. Cada controlador debe poder consultar el estado de los demás controladores en cualquier momento y cada uno contiene una instancia de vector<weak_ptr<Controller>> privada para este fin. Cada vector contiene una referencia circular y, por lo tanto, se usan instancias de weak_ptr en lugar de shared_ptr.

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class Controller
{
public:
   int Num;
   wstring Status;
   vector<weak_ptr<Controller>> others;
   explicit Controller(int i) : Num(i), Status(L"On")
   {
      wcout << L"Creating Controller" << Num << endl;
   }

   ~Controller()
   {
      wcout << L"Destroying Controller" << Num << endl;
   }

   // Demonstrates how to test whether the
   // pointed-to memory still exists or not.
   void CheckStatuses() const
   {
      for_each(others.begin(), others.end(), [](weak_ptr<Controller> wp) {
         auto p = wp.lock();
         if (p)
         {
            wcout << L"Status of " << p->Num << " = " << p->Status << endl;
         }
         else
         {
            wcout << L"Null object" << endl;
         }
      });
   }
};

void RunTest()
{
   vector<shared_ptr<Controller>> v{
       make_shared<Controller>(0),
       make_shared<Controller>(1),
       make_shared<Controller>(2),
       make_shared<Controller>(3),
       make_shared<Controller>(4),
   };

   // Each controller depends on all others not being deleted.
   // Give each controller a pointer to all the others.
   for (int i = 0; i < v.size(); ++i)
   {
      for_each(v.begin(), v.end(), [&v, i](shared_ptr<Controller> p) {
         if (p->Num != i)
         {
            v[i]->others.push_back(weak_ptr<Controller>(p));
            wcout << L"push_back to v[" << i << "]: " << p->Num << endl;
         }
      });
   }

   for_each(v.begin(), v.end(), [](shared_ptr<Controller> &p) {
      wcout << L"use_count = " << p.use_count() << endl;
      p->CheckStatuses();
   });
}

int main()
{
   RunTest();
   wcout << L"Press any key" << endl;
   char ch;
   cin.getline(&ch, 1);
}
Creating Controller0
Creating Controller1
Creating Controller2
Creating Controller3
Creating Controller4
push_back to v[0]: 1
push_back to v[0]: 2
push_back to v[0]: 3
push_back to v[0]: 4
push_back to v[1]: 0
push_back to v[1]: 2
push_back to v[1]: 3
push_back to v[1]: 4
push_back to v[2]: 0
push_back to v[2]: 1
push_back to v[2]: 3
push_back to v[2]: 4
push_back to v[3]: 0
push_back to v[3]: 1
push_back to v[3]: 2
push_back to v[3]: 4
push_back to v[4]: 0
push_back to v[4]: 1
push_back to v[4]: 2
push_back to v[4]: 3
use_count = 1
Status of 1 = On
Status of 2 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 2 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 2 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 2 = On
Status of 3 = On
Destroying Controller0
Destroying Controller1
Destroying Controller2
Destroying Controller3
Destroying Controller4
Press any key

Como experimento, modifique el vector others para que sea una instancia de vector<shared_ptr<Controller>> y, luego, en la salida, observe que no se invoca ningún destructor cuando RunTest devuelve resultados.

Consulte también

Punteros inteligentes (C++ moderno)