Freigeben über


Gewusst wie: Erstellen und Verwenden von weak_ptr-Instanzen

Manchmal muss ein Objekt eine Möglichkeit speichern, um auf das shared_ptr zugrunde liegende Objekt zuzugreifen, ohne dadurch den Referenzzähler zu erhöhen. Diese Situation tritt in der Regel auf, wenn zyklische Verweise zwischen shared_ptr-Instanzen vorliegen.

Der optimale Entwurf ist die Vermeidung von gemeinsamem Zeigerbesitz. Wenn Sie jedoch gemeinsamen Besitz von shared_ptr-Instanzen benötigen, vermeiden Sie zyklische Verweise zwischen ihnen. Wenn zyklische Verweise unvermeidbar oder aus irgendeinem Grund sogar vorzuziehen sind, verwenden Sie weak_ptr, um einem oder mehreren der Besitzer einen schwachen Verweis auf einen anderen shared_ptr zu geben. Mit weak_ptr können Sie einen shared_ptr erstellen, der eine vorhandene Gruppe verwandter Instanzen verknüpft, wobei nur die zugrunde liegende Speicherressource weiterhin gültig ist. Der weak_ptr selbst nimmt nicht an der Referenzzählung teil und kann daher nicht verhindern, dass der Referenzzähler den Wert 0 erreicht. Sie können jedoch einen weak_ptr verwenden, um zu versuchen, eine neue Kopie des shared_ptr zu erhalten, mit der er initialisiert wurde. Wenn der Arbeitsspeicher bereits gelöscht wurde, wird eine bad_weak_ptr-Ausnahme ausgelöst. Wenn der Arbeitsspeicher noch gültig ist, erhöht der neue freigegebene Zeiger den Verweiszähler und stellt sicher, dass der Arbeitsspeicher gültig bleibt, solange die shared_ptr-Variable im Gültigkeitsbereich liegt.

Beispiel

Das folgende Codebeispiel zeigt einen Fall, in dem weak_ptr verwendet wird, um das richtige Löschen von Objekten sicherzustellen, die Ringabhängigkeiten aufweisen. Nehmen Sie für das Beispiel an, dass es erst erstellt wurde, nachdem keine alternativen Lösungen gefunden werden konnten. Die Controller-Objekte stellen einige Aspekte eines Computerprozesses dar, und sie funktionieren unabhängig. Jeder Controller muss in der Lage sein, den Status der anderen Controller jederzeit abzufragen, und jeder enthält zu diesem Zweck einen privaten vector<weak_ptr<Controller>>. Jeder Vektor enthält einen Zirkelverweis. Daher werden weak_ptr-Instanzen anstelle von shared_ptr verwendet.

#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);
}
  

Ändern Sie testweise den Vektor others in einen vector<shared_ptr<Controller>>, und beachten Sie dann in der Ausgabe, dass keine Destruktoren aufgerufen wurden, als TestRun zurückkehrte.

Siehe auch

Konzepte

Intelligente Zeiger (Modern C++)