Поделиться через


Практическое руководство. Создание и использование экземпляров weak_ptr

Иногда объект должен хранить способ доступа к базовому объекту shared_ptr без увеличения числа ссылок. Как правило, эта ситуация возникает при наличии циклических ссылок между shared_ptr экземплярами.

Лучший дизайн заключается в том, чтобы избежать общего владения указателями всякий раз, когда вы можете. Однако если у вас должна быть общая собственность shared_ptr на экземпляры, избегайте циклических ссылок между ними. Если циклические ссылки неизбежны или даже предпочтительнее по какой-либо причине, используйте weak_ptr , чтобы дать одному или нескольким владельцам слабую ссылку на другое shared_ptr. С помощью weak_ptrэлемента управления можно создать shared_ptr соединение с существующим набором связанных экземпляров, но только в том случае, если базовый ресурс памяти по-прежнему действителен. Само weak_ptr по себе не участвует в подсчете ссылок, поэтому оно не может запретить подсчет ссылок нулю. Однако можно использовать weak_ptr попытку получить новую копию shared_ptr , с которой она была инициализирована. Если память уже удалена, weak_ptrоператор bool возвращается false. Если память по-прежнему действительна, новый общий указатель увеличивает число ссылок и гарантирует, что память будет действительна до тех пор, пока shared_ptr переменная остается в область.

Пример

В следующем примере кода показан случай, когда weak_ptr используется для обеспечения правильного удаления объектов с циклическими зависимостями. Рассмотрим пример, предположим, что он был создан только после рассмотрения альтернативных решений. Объекты Controller представляют некоторые аспекты процесса компьютера, и они работают независимо. Каждый контроллер должен иметь возможность запрашивать состояние других контроллеров в любое время, и каждый из них содержит частный vector<weak_ptr<Controller>> для этой цели. Каждый вектор содержит циклическую ссылку, поэтому weak_ptr экземпляры используются вместо 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

В качестве эксперимента измените вектор на значение vector<shared_ptr<Controller>>, а затем в выходных данных обратите внимание, что деструкторы others не вызываются при RunTest возврате.

См. также

Интеллектуальные указатели (современный C++)