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


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

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

Лучший подход к разработке — избегать совместного владения указателей, если можно. Однако если необходимо иметь общее владение экземплярами shared_ptr, избегайте цикличных связей между ними. При цикловой ссылки неизбежны или даже предпочтительны по какой-то причине использовать weak_ptr для одного или нескольких владельцев слабую ссылку на другой shared_ptr. Используя weak_ptr, можно создать shared_ptr, который присоединяется к существующему набору связанных экземпляров, но только при условии, что базовый ресурс памяти по-прежнему является допустимым. weak_ptr сам по себе не участвует в подсчете ссылок, поэтому он не может предотвратить нулевое число ссылок. Однако weak_ptr можно использовать для получения доступа к новой копии shared_ptr, с которым она была инициализирована. Если память уже была удалена, то возникает исключение bad_weak_ptr. Если память по-прежнему является допустимой, новый общий указатель увеличивает счетчик ссылок и гарантирует, что память будет допустима, пока переменная 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)
        {
            try
            {
                auto p = wp.lock();
                wcout << L"Status of " << p->Num << " = " << p->Status << endl;
            }

            catch (bad_weak_ptr b)
            {
                wcout << L"Null object" << endl;
            }                
        });
    }
};

void RunTest()
{
    vector<shared_ptr<Controller>> v;

    v.push_back(shared_ptr<Controller>(new Controller(0)));
    v.push_back(shared_ptr<Controller>(new Controller(1)));
    v.push_back(shared_ptr<Controller>(new Controller(2)));
    v.push_back(shared_ptr<Controller>(new Controller(3)));
    v.push_back(shared_ptr<Controller>(new 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);
}
  

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

См. также

Основные понятия

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