Condividi tramite


Procedura: Creare e usare istanze di weak_ptr

In alcuni casi un oggetto deve archiviare un modo per accedere all'oggetto sottostante di un shared_ptr senza che il conteggio dei riferimenti venga incrementato. In genere, questa situazione si verifica quando sono presenti riferimenti ciclici tra shared_ptr istanze.

La progettazione migliore consiste nell'evitare la proprietà condivisa dei puntatori ogni volta che è possibile. Tuttavia, se è necessario avere la proprietà condivisa delle shared_ptr istanze, evitare riferimenti ciclici tra di essi. Quando i riferimenti ciclici sono inevitabili o addirittura preferibili per qualche motivo, usare weak_ptr per assegnare a uno o più proprietari un riferimento debole a un altro shared_ptr. Usando un weak_ptroggetto , è possibile creare un oggetto shared_ptr che unisce a un set esistente di istanze correlate, ma solo se la risorsa di memoria sottostante è ancora valida. Un weak_ptr oggetto stesso non partecipa al conteggio dei riferimenti e pertanto non può impedire al conteggio dei riferimenti di passare a zero. Tuttavia, è possibile usare un weak_ptr oggetto per provare a ottenere una nuova copia dell'oggetto shared_ptr con cui è stato inizializzato. Se la memoria è già stata eliminata, l'operatore bool dell'oggetto weak_ptrrestituisce false. Se la memoria è ancora valida, il nuovo puntatore condiviso incrementa il conteggio dei riferimenti e garantisce che la memoria sia valida purché la shared_ptr variabile rimanga nell'ambito.

Esempio

Nell'esempio di codice seguente viene illustrato un caso in cui weak_ptr viene usato per garantire l'eliminazione corretta di oggetti con dipendenze circolari. Mentre si esamina l'esempio, si supponga che sia stato creato solo dopo che sono state considerate soluzioni alternative. Gli Controller oggetti rappresentano alcuni aspetti di un processo di macchina e operano in modo indipendente. Ogni controller deve essere in grado di eseguire query sullo stato degli altri controller in qualsiasi momento e ognuno di essi contiene un privato vector<weak_ptr<Controller>> a questo scopo. Ogni vettore contiene un riferimento circolare e pertanto vengono weak_ptr usate istanze anziché 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

Come esperimento, modificare il vettore others come e vector<shared_ptr<Controller>>quindi nell'output, notare che non vengono richiamati distruttori quando RunTest viene restituito .

Vedi anche

Puntatori intelligenti (C++ moderno)