Partager via


Procédure : Créer et utiliser des instances weak_ptr

Parfois, un objet doit stocker un moyen d’accéder à l’objet sous-jacent d’une shared_ptr sans entraîner l’incrémentation du nombre de références. En règle générale, cette situation se produit lorsque vous avez des références cycliques entre shared_ptr des instances.

La meilleure conception consiste à éviter la propriété partagée des pointeurs chaque fois que vous le pouvez. Toutefois, si vous devez avoir la propriété partagée des shared_ptr instances, évitez les références cycliques entre elles. Lorsque les références cycliques sont inévitables, voire préférables pour une raison quelconque, utilisez weak_ptr pour donner une ou plusieurs des propriétaires une référence faible à une autre shared_ptr. À l’aide d’un weak_ptr, vous pouvez créer une shared_ptr jointure à un ensemble existant d’instances associées, mais uniquement si la ressource de mémoire sous-jacente est toujours valide. Un weak_ptr lui-même ne participe pas au comptage de références, et par conséquent, il ne peut pas empêcher le nombre de références d’aller à zéro. Toutefois, vous pouvez utiliser un weak_ptr outil pour essayer d’obtenir une nouvelle copie du shared_ptr fichier avec lequel il a été initialisé. Si la mémoire a déjà été supprimée, l’opérateur weak_ptrbool 's bool retourne false. Si la mémoire est toujours valide, le nouveau pointeur partagé incrémente le nombre de références et garantit que la mémoire sera valide tant que la variable reste dans l’étendue shared_ptr .

Exemple

L’exemple de code suivant montre un cas où weak_ptr est utilisé pour garantir la suppression appropriée d’objets qui ont des dépendances circulaires. Comme vous examinez l’exemple, supposons qu’il a été créé uniquement après que d’autres solutions ont été prises en compte. Les Controller objets représentent un aspect d’un processus machine et fonctionnent indépendamment. Chaque contrôleur doit être en mesure d’interroger l’état des autres contrôleurs à tout moment, et chacun d’eux contient un privé vector<weak_ptr<Controller>> à cet effet. Chaque vecteur contient une référence circulaire, et par conséquent, weak_ptr les instances sont utilisées au lieu 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

En tant qu’expérience, modifiez le vecteur others pour qu’il soit un vector<shared_ptr<Controller>>, puis dans la sortie, notez qu’aucun destructeur n’est appelé quand il RunTest est retourné.

Voir aussi

Pointeurs intelligents (C++ moderne)