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_ptraus einem Zeiger. - Beim Erstellen eines
std::shared_ptrmit einem benutzerdefinierten Deleter oder Allocator. - Bei Verwendung von
std::make_sharedoderstd::allocate_shared.
Dieser Kontrollblock enthält mindestens zwei Zähler:
- Starke Referenzzählung (strong count): Wird erhöht, wenn ein
std::shared_ptrerstellt oder kopiert wird. Wird verringert, wenn einstd::shared_ptrzerstört wird. Wenn dieser Zähler null erreicht, wird das verwaltete Objekt freigegeben. - Schwache Referenzzählung (weak count): Wird erhöht, wenn ein
std::weak_ptraus einemstd::shared_ptrerstellt wird. Wird verringert, wenn einstd::weak_ptrzerstö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