Sobes.tech
Middle

Wie funktioniert der Referenzzähler in std::shared_ptr?

sobes.tech KI

Antwort von AI

Der Referenzzähler in std::shared_ptr wird in einem separaten Objekt gespeichert: im Kontrollblock (control block).

Der Kontrollblock wird erstellt:

  • Beim ersten Erstellen eines std::shared_ptr aus einem Zeiger.
  • Beim Erstellen eines std::shared_ptr mit einem benutzerdefinierten Deleter oder Allocator.
  • Bei Verwendung von std::make_shared oder std::allocate_shared.

Dieser Kontrollblock enthält mindestens zwei Zähler:

  1. Starke Referenzzählung (strong count): Wird erhöht, wenn ein std::shared_ptr erstellt oder kopiert wird. Wird verringert, wenn ein std::shared_ptr zerstört wird. Wenn dieser Zähler null erreicht, wird das verwaltete Objekt freigegeben.
  2. Schwache Referenzzählung (weak count): Wird erhöht, wenn ein std::weak_ptr aus einem std::shared_ptr erstellt wird. Wird verringert, wenn ein std::weak_ptr zerstört wird. Der Kontrollblock wird freigegeben, wenn beide Zähler — stark und schwach — null sind.

Die Verwendung von std::make_shared ist vorzuziehen gegenüber direkter Erstellung mit new, da es das Objekt und den Kontrollblock in einem einzigen Speicherblock allokieren kann, was die Leistung verbessert und Fragmentierung reduziert.

Die Zähler sind atomar, was std::shared_ptr in Multithread-Szenarien sicher macht, obwohl der Zugriff auf das verwaltete Objekt standardmäßig nicht synchronisiert ist.

Beispiel:

#include <iostream>
#include <memory>

struct MyObject {
    int data;
    MyObject(int d) : data(d) {
        std::cout << "MyObject(" << data << ") erstellt\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") zerstört\n";
    }
};

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // starker Zähler: 1, schwacher Zähler: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // starker Zähler: 2, schwacher Zähler: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // starker Zähler: 2, schwacher Zähler: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // starker Zähler: 3, schwacher Zähler: 1 (temporär innerhalb von lock)
            std::cout << "Objektdaten: " << locked_ptr->data << std::endl;
        }
        // starker Zähler: 2, schwacher Zähler: 1
    } // ptr2 verlässt den Gültigkeitsbereich
    // starker Zähler: 1, schwacher Zähler: 1

    // Das Objekt wurde noch nicht zerstört, da der starke Zähler == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // starker Zähler: 1, schwacher Zähler: 2
    } // weak_ptr2 verlässt den Gültigkeitsbereich
    // starker Zähler: 1, schwacher Zähler: 1

} // ptr1 verlässt den Gültigkeitsbereich
// starker Zähler: 0, schwacher Zähler: 1
// Das Objekt wird zerstört, da der starke Zähler 0 erreicht hat
// Der Kontrollblock bleibt, bis der schwache Zähler 0 ist