Namespaces
Variants

std:: shared_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
Definiert im Header <memory>
template < class T > class shared_ptr ;
(seit C++11)

std::shared_ptr ist ein intelligenter Zeiger, der gemeinsamen Besitz eines Objekts über einen Zeiger aufrechterhält. Mehrere shared_ptr Objekte können dasselbe Objekt besitzen. Das Objekt wird zerstört und sein Speicher freigegeben, wenn eines der folgenden Ereignisse eintritt:

  • der letzte verbleibende shared_ptr , der das Objekt besitzt, wird zerstört;
  • der letzte verbleibende shared_ptr , der das Objekt besitzt, wird über operator= oder reset() ein anderer Zeiger zugewiesen.

Das Objekt wird mittels eines delete-expression oder eines benutzerdefinierten Löschers zerstört, der dem shared_ptr während der Konstruktion übergeben wird.

Ein shared_ptr kann die Eigentümerschaft eines Objekts teilen, während er auf ein anderes Objekt zeigt. Diese Funktion kann verwendet werden, um auf Member-Objekte zu zeigen, während das Objekt, zu dem sie gehören, im Besitz bleibt. Der gespeicherte Zeiger ist derjenige, auf den durch get() , die Dereferenzierungs- und Vergleichsoperatoren zugegriffen wird. Der verwaltete Zeiger ist derjenige, der an den Deleter übergeben wird, wenn die Verwendungsanzahl null erreicht.

Ein shared_ptr kann auch keine Objekte besitzen, in diesem Fall wird er als leer bezeichnet (ein leerer shared_ptr kann einen nicht-null gespeicherten Zeiger haben, wenn der Aliasing-Konstruktor zu seiner Erzeugung verwendet wurde).

Alle Spezialisierungen von shared_ptr erfüllen die Anforderungen von CopyConstructible , CopyAssignable und LessThanComparable und sind kontextuell konvertierbar zu bool .

Alle Memberfunktionen (einschließlich Kopierkonstruktor und Kopierzuweisung) können von mehreren Threads auf verschiedenen shared_ptr Objekten ohne zusätzliche Synchronisierung aufgerufen werden, selbst wenn diese Objekte Kopien sind und sich das Eigentum am selben Objekt teilen. Wenn mehrere Ausführungsthreads auf dasselbe shared_ptr Objekt ohne Synchronisierung zugreifen und einer dieser Zugriffe eine nicht-konstante Memberfunktion von shared_ptr verwendet, tritt ein Datenwettlauf auf; der std::atomic<shared_ptr> kann verwendet werden, um den Datenwettlauf zu verhindern.

Inhaltsverzeichnis

Mitgliedertypen

Mitgliedertyp Definition
element_type
T (bis C++17)
std:: remove_extent_t < T > (seit C++17)
weak_type (seit C++17) std:: weak_ptr < T >

Memberfunktionen

Konstruiert neuen shared_ptr
(öffentliche Elementfunktion)
zerstört das verwaltete Objekt, falls keine weiteren shared_ptr darauf verweisen
(öffentliche Elementfunktion)
weist den shared_ptr zu
(öffentliche Elementfunktion)
Modifikatoren
ersetzt das verwaltete Objekt
(öffentliche Elementfunktion)
tauscht die verwalteten Objekte
(öffentliche Elementfunktion)
Beobachter
gibt den gespeicherten Zeiger zurück
(öffentliche Elementfunktion)
dereferenziert den gespeicherten Zeiger
(öffentliche Elementfunktion)
(C++17)
bietet indizierten Zugriff auf das gespeicherte Array
(öffentliche Elementfunktion)
gibt die Anzahl der shared_ptr -Objekte zurück, die auf dasselbe verwaltete Objekt verweisen
(öffentliche Elementfunktion)
(until C++20)
prüft, ob das verwaltete Objekt nur vom aktuellen shared_ptr -Objekt verwaltet wird
(öffentliche Elementfunktion)
prüft, ob der gespeicherte Zeiger nicht null ist
(öffentliche Elementfunktion)
bietet besitzerbasierte Ordnung von Shared Pointern
(öffentliche Elementfunktion)
(C++26)
bietet besitzerbasiertes Hashing von Shared Pointern
(öffentliche Elementfunktion)
bietet besitzerbasierte Gleichheitsvergleiche von Shared Pointern
(öffentliche Elementfunktion)

Nicht-Member-Funktionen

erstellt einen shared_ptr, der ein neues Objekt verwaltet
(Funktions-Template)
erstellt einen shared_ptr, der ein neues Objekt verwaltet, das mit einem Allokator alloziert wurde
(Funktions-Template)
wendet static_cast , dynamic_cast , const_cast , oder reinterpret_cast auf den gespeicherten Zeiger an
(Funktions-Template)
gibt den Deleter des angegebenen Typs zurück, falls vorhanden
(Funktions-Template)
(entfernt in C++20) (entfernt in C++20) (entfernt in C++20) (entfernt in C++20) (entfernt in C++20) (C++20)
vergleicht mit einem anderen shared_ptr oder mit nullptr
(Funktions-Template)
gibt den Wert des gespeicherten Zeigers an einen Ausgabestrom aus
(Funktions-Template)
spezialisiert den std::swap Algorithmus
(Funktions-Template)
spezialisiert atomare Operationen für std::shared_ptr
(Funktionstemplate)

Hilfsklassen

atomarer Shared Pointer
(Klassen-Template-Spezialisierung)
Hash-Unterstützung für std::shared_ptr
(Klassen-Template-Spezialisierung)

Deduction guides (seit C++17)

Hinweise

Der Besitz eines Objekts kann nur durch Konstruktions- oder Zuweisungsfunktionen geteilt werden, die ein anderes shared_ptr -Objekt empfangen. Wenn ein neuer shared_ptr ausschließlich unter Verwendung des rohen zugrundeliegenden Zeigers erstellt wird, der von einem anderen shared_ptr gehalten wird, geht dieser neue shared_ptr davon aus, dass keine anderen shared_ptr -Instanzen das Objekt besitzen, das er hält. Dies führt dazu (sofern keine nachfolgende Zuweisung erfolgt), dass der Deleter während der Zerstörung wiederholt auf dasselbe Objekt angewendet wird.

std::shared_ptr kann mit einem unvollständigen Typ T verwendet werden. Allerdings dürfen der Konstruktor aus einem rohen Zeiger ( template < class Y > shared_ptr ( Y * ) ) und die template < class Y > void reset ( Y * ) Member-Funktion nur mit einem Zeiger auf einen vollständigen Typ aufgerufen werden (beachten Sie, dass std::unique_ptr aus einem rohen Zeiger auf einen unvollständigen Typ konstruiert werden kann).

Das T in std :: shared_ptr < T > kann ein Funktionstyp sein: In diesem Fall verwaltet es einen Zeiger auf eine Funktion, anstatt eines Objektzeigers. Dies wird manchmal verwendet, um eine dynamische Bibliothek oder ein Plugin geladen zu halten, solange irgendeine ihrer Funktionen referenziert wird:

void del(void(*)()) {}
void fun() {}
int main()
{
    std::shared_ptr<void()> ee(fun, del);
    (*ee)();
}

Implementierungshinweise

In einer typischen Implementierung, shared_ptr enthält nur zwei Zeiger:

  • der gespeicherte Zeiger (der von get() zurückgegebene);
  • ein Zeiger auf den Kontrollblock .

Der Kontrollblock ist ein dynamisch allokiertes Objekt, das Folgendes enthält:

  • entweder einen Zeiger auf das verwaltete Objekt oder das verwaltete Objekt selbst;
  • den Deleter (typlöscht);
  • den Allokator (typlöscht);
  • die Anzahl der shared_ptr s, die das verwaltete Objekt besitzen;
  • die Anzahl der weak_ptr s, die auf das verwaltete Objekt verweisen.

Wenn shared_ptr durch Aufruf von std::make_shared oder std::allocate_shared erstellt wird, wird der Speicher für sowohl den Kontrollblock als auch das verwaltete Objekt mit einer einzigen Allokation erstellt. Das verwaltete Objekt wird direkt in einem Datenelement des Kontrollblocks konstruiert. Wenn shared_ptr über einen der shared_ptr -Konstruktoren erstellt wird, müssen der verwaltete Objekt und der Kontrollblock separat allokiert werden. In diesem Fall speichert der Kontrollblock einen Zeiger auf das verwaltete Objekt.

Der vom shared_ptr direkt gehaltene Zeiger ist derjenige, der von get() zurückgegeben wird, während der im Kontrollblock gehaltene Zeiger/das Objekt derjenige ist, der gelöscht wird, wenn die Anzahl der gemeinsamen Besitzer null erreicht. Diese Zeiger sind nicht notwendigerweise gleich.

Der Destruktor von shared_ptr dekrementiert die Anzahl der gemeinsamen Eigentümer des Kontrollblocks. Wenn dieser Zähler null erreicht, ruft der Kontrollblock den Destruktor des verwalteten Objekts auf. Der Kontrollblock gibt sich selbst erst frei, wenn der std::weak_ptr -Zähler ebenfalls null erreicht.

In bestehenden Implementierungen wird die Anzahl der Weak Pointer erhöht ( [1] , [2] ), falls ein Shared Pointer auf denselben Kontrollblock existiert.

Um die Anforderungen an Thread-Sicherheit zu erfüllen, werden die Referenzzähler typischerweise mittels eines Äquivalents von std::atomic::fetch_add mit std::memory_order_relaxed inkrementiert (das Dekrementieren erfordert eine stärkere Ordnung zur sicheren Zerstörung des Kontrollblocks).

Beispiel

#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
using namespace std::chrono_literals;
struct Base
{
    Base() { std::cout << "Base::Base()\n"; }
    // Hinweis: nicht-virtueller Destruktor ist hier in Ordnung
    ~Base() { std::cout << "Base::~Base()\n"; }
};
struct Derived : public Base
{
    Derived() { std::cout << "Derived::Derived()\n"; }
    ~Derived() { std::cout << "Derived::~Derived()\n"; }
};
void print(auto rem, std::shared_ptr<Base> const& sp)
{
    std::cout << rem << "\n\tget() = " << sp.get()
              << ", use_count() = " << sp.use_count() << '\n';
}
void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(987ms);
    std::shared_ptr<Base> lp = p; // threadsicher, selbst wenn der
                                  // gemeinsame use_count erhöht wird
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        print("Lokaler Zeiger in einem Thread:", lp);
    }
}
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
    print("Erstellt einen gemeinsamen Derived (als Zeiger auf Base)", p);
    std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
    p.reset(); // Besitzrecht von main freigeben
    print("Gemeinsamer Besitz zwischen 3 Threads und freigegebener Besitz von main:", p);
    t1.join();
    t2.join();
    t3.join();
    std::cout << "Alle Threads abgeschlossen, der letzte hat Derived gelöscht.\n";
}

Mögliche Ausgabe:

Base::Base()
Derived::Derived()
Erstellt einen gemeinsamen Derived (als Zeiger auf Base)
	get() = 0x118ac30, use_count() = 1
Gemeinsamer Besitz zwischen 3 Threads und freigegebener Besitz von main:
	get() = 0, use_count() = 0
Lokaler Zeiger in einem Thread:
	get() = 0x118ac30, use_count() = 5
Lokaler Zeiger in einem Thread:
	get() = 0x118ac30, use_count() = 4
Lokaler Zeiger in einem Thread:
	get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
Alle Threads abgeschlossen, der letzte hat Derived gelöscht.

Beispiel

#include <iostream>
#include <memory>
struct MyObj
{
    MyObj() { std::cout << "MyObj konstruiert\n"; }
    ~MyObj() { std::cout << "MyObj zerstört\n"; }
};
struct Container : std::enable_shared_from_this<Container> // Hinweis: öffentliche Vererbung
{
    std::shared_ptr<MyObj> memberObj;
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // Verwende einen Alias Shared Pointer für Member
        return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
    }
};
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
    COUT("Erstellen eines gemeinsamen Containers");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Erstellen von Mitgliedern");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Erstellen eines weiteren gemeinsamen Containers");
    std::shared_ptr<Container> cont2 = cont;
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("GetAsMyObj");
    std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("Kopieren des Alias-Objekts");
    std::shared_ptr<MyObj> myobj2 = myobj1;
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("Zurücksetzen von cont2");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Zurücksetzen von myobj2");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("Zurücksetzen cont");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

Ausgabe:

Erstellen eines gemeinsamen Containers
cont.use_count() = 1
cont->memberObj.use_count() = 0
Erstellen eines Members
MyObj konstruiert
cont.use_count() = 1
cont->memberObj.use_count() = 1
Erstellen eines weiteren gemeinsamen Containers
cont.use_count() = 2
cont->memberObj.use_count() = 1
cont2.use_count() = 2
cont2->memberObj.use_count() = 1
GetAsMyObj
myobj1.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
cont2.use_count() = 3
cont2->memberObj.use_count() = 1
Kopieren des Alias-Objekts
myobj1.use_count() = 4
myobj2.use_count() = 4
cont.use_count() = 4
cont->memberObj.use_count() = 1
cont2.use_count() = 4
cont2->memberObj.use_count() = 1
Zurücksetzen von cont2
myobj1.use_count() = 3
myobj2.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
Zurücksetzen von myobj2
myobj1.use_count() = 2
cont.use_count() = 2
cont->memberObj.use_count() = 1
Zurücksetzen von cont
myobj1.use_count() = 1
cont.use_count() = 0
MyObj zerstört

Siehe auch

(C++11)
Intelligenter Zeiger mit eindeutiger Objektbesitz-Semantik
(Klassentemplate)
(C++11)
Schwacher Verweis auf ein von std::shared_ptr verwaltetes Objekt
(Klassentemplate)