Namensräume
Varianten
Aktionen

std::shared_ptr

Von cppreference.com
< cpp‎ | memory
 
 
Speicherverwaltungsbibliothek
(nur Exposition*)
Algorithmen für uninitialisierten Speicher
(C++17)
(C++17)
(C++17)
Beschränkte uninitialisierte
Speicher-Algorithmen
C-Bibliothek

Allocatoren
Speicherressourcen
Unterstützung für Garbage Collection
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
(C++11)(bis C++23)
Uninitialisierter Speicher
Explizites Lebenszeitmanagement
 
 
Definiert in Header <memory>
template< class T > class shared_ptr;
(seit C++11)

std::shared_ptr ist ein Smart-Pointer, der eine geteilte Besitzrechte an einem Objekt über einen Zeiger verwaltet. 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, zerstört wird;
  • dem letzten verbleibenden shared_ptr, der das Objekt besitzt, über operator= oder reset() ein anderer Zeiger zugewiesen wird.

Das Objekt wird mit einem delete-Ausdruck oder einem benutzerdefinierten Deleter, der shared_ptr während der Konstruktion übergeben wird, zerstört.

Ein shared_ptr kann den Besitz eines Objekts teilen, während er einen Zeiger auf ein anderes Objekt speichert. Diese Funktion kann verwendet werden, um auf Member-Objekte zu zeigen, während das Objekt, zu dem sie gehören, besessen wird. Der gespeicherte Zeiger ist derjenige, auf den mit get(), den Dereferenzierungs- und Vergleichsoperatoren zugegriffen wird. Der verwaltete Zeiger ist derjenige, der an den Deleter übergeben wird, wenn die Gebrauchszählung 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 Alias-Konstruktor zu seiner Erstellung verwendet wurde).

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

Alle Member-Funktionen (einschließlich Kopierkonstruktor und Kopierzuweisung) können von mehreren Threads auf unterschiedliche shared_ptr-Objekte aufgerufen werden, ohne zusätzliche Synchronisation, auch wenn diese Objekte Kopien sind und den Besitz desselben Objekts teilen. Wenn mehrere Threads auf dasselbe shared_ptr-Objekt ohne Synchronisation zugreifen und einer dieser Zugriffe eine nicht-const Member-Funktion von shared_ptr verwendet, tritt ein Datenrennen auf; std::atomic<shared_ptr> kann verwendet werden, um das Datenrennen zu verhindern.

Inhalt

[edit] Member-Typen

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>

[edit] Memberfunktionen

erstellt einen neuen shared_ptr
(public member function) [edit]
zerstört das besessene Objekt, wenn keine weiteren shared_ptrs darauf verweisen
(public member function) [edit]
weist den shared_ptr zu
(public member function) [edit]
Modifizierer
Ersetzt das verwaltete Objekt
(public member function) [edit]
Vertauscht die verwalteten Objekte
(public member function) [edit]
Observer
gibt den gespeicherten Zeiger zurück
(public member function) [edit]
dereferenziert den gespeicherten Zeiger
(public member function) [edit]
ermöglicht den indizierten Zugriff auf das gespeicherte Array
(public member function) [edit]
gibt die Anzahl der shared_ptr-Objekte zurück, die auf dasselbe verwaltete Objekt verweisen
(public member function) [edit]
(bis C++20)
prüft, ob das verwaltete Objekt nur vom aktuellen shared_ptr-Objekt verwaltet wird
(public member function) [edit]
prüft, ob der gespeicherte Zeiger nicht null ist
(public member function) [edit]
bietet inhaltsbasiertes Ordering von geteilten Zeigern
(public member function) [edit]
ermöglicht besitzerbasiertes Hashing von shared_ptr
(public member function) [edit]
ermöglicht besitzerbasierten Gleichheitsvergleich von shared_ptr
(public member function) [edit]

[edit] Nicht-Member-Funktionen

erstellt einen geteilten Zeiger, der ein neues Objekt verwaltet
(Funktionsschablone) [bearbeiten]
erstellt einen geteilten Zeiger, der ein neues Objekt verwaltet, das mithilfe eines Allokators zugewiesen wurde
(Funktionsschablone) [bearbeiten]
wendet static_cast, dynamic_cast, const_cast oder reinterpret_cast auf den gespeicherten Zeiger an
(Funktionsschablone) [bearbeiten]
gibt den Deleter des angegebenen Typs zurück, falls vorhanden
(Funktionsschablone) [bearbeiten]
(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
(Funktionsschablone) [bearbeiten]
gibt den Wert des gespeicherten Zeigers an einen Ausgabestrom aus
(Funktionsschablone) [bearbeiten]
spezialisiert den Algorithmus std::swap
(Funktionsschablone) [bearbeiten]
spezialisiert atomare Operationen für std::shared_ptr
(Funktionsschablone) [bearbeiten]

[edit] Hilfsklassen

atomarer geteilter Zeiger
(Klassenvorlagenspezialisierung) [bearbeiten]
Hash-Unterstützung für std::shared_ptr
(Klassentemplate-Spezialisierung) [bearbeiten]

[edit] Deduktionsleitfäden (seit C++17)

[edit] Hinweise

Der Besitz eines Objekts kann nur durch Kopierkonstruktion oder Kopierzuweisung seines Wertes an einen anderen shared_ptr mit einem anderen shared_ptr geteilt werden. Das Erstellen eines neuen shared_ptr mit dem rohen zugrundeliegenden Zeiger eines anderen shared_ptr führt zu undefiniertem Verhalten.

std::shared_ptr kann mit einem unvollständigen Typ T verwendet werden. Der Konstruktor von einem rohen Zeiger (template<class Y> shared_ptr(Y*)) und die Member-Funktion template<class Y> void reset(Y*) dürfen jedoch 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, nicht einen Objektzeiger. Dies wird manchmal verwendet, um eine dynamische Bibliothek oder ein Plugin geladen zu halten, solange auf eine seiner Funktionen zugegriffen wird

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

[edit] Implementierungshinweise

In einer typischen Implementierung hält shared_ptr nur zwei Zeiger

  • den gespeicherten Zeiger (der von get() zurückgegeben wird);
  • einen Zeiger auf einen Kontrollblock.

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

  • entweder einen Zeiger auf das verwaltete Objekt oder das verwaltete Objekt selbst;
  • den Deleter (typ-erased);
  • den Allocator (typ-erased);
  • die Anzahl der shared_ptrs, die das verwaltete Objekt besitzen;
  • die Anzahl der weak_ptrs, die auf das verwaltete Objekt verweisen.

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

Der direkt von shared_ptr gehaltene Zeiger ist derjenige, der von get() zurückgegeben wird, während der Zeiger/das Objekt, das vom Kontrollblock gehalten wird, dasjenige ist, das zerstört wird, wenn die Anzahl der gemeinsamen Besitzer Null erreicht. Diese Zeiger müssen nicht unbedingt gleich sein.

Der Destruktor von shared_ptr dekrementiert die Anzahl der gemeinsamen Besitzer des Kontrollblocks. Wenn dieser Zähler Null erreicht, ruft der Kontrollblock den Destruktor des verwalteten Objekts auf. Der Kontrollblock gibt sich selbst nicht frei, bis auch der std::weak_ptr-Zähler Null erreicht.

In existierenden Implementierungen wird die Anzahl der Weak-Pointer inkrementiert ([1], [2]), wenn ein Shared-Pointer auf denselben Kontrollblock existiert.

Um die Thread-Sicherheitsanforderungen zu erfüllen, werden die Referenzzähler typischerweise mithilfe eines Äquivalents von std::atomic::fetch_add mit std::memory_order_relaxed inkrementiert (das Dekrementieren erfordert eine stärkere Ordnung, um den Kontrollblock sicher zu zerstören).

[edit] Beispiel

#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
 
using namespace std::chrono_literals;
 
struct Base
{
    Base() { std::cout << "Base::Base()\n"; }
 
    // Note: non-virtual destructor is OK here
    ~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; // thread-safe, even though the
                                  // shared use_count is incremented
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        print("Local pointer in a thread:", lp);
    }
}
 
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
 
    print("Created a shared Derived (as a pointer to Base)", p);
 
    std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
    p.reset(); // release ownership from main
 
    print("Shared ownership between 3 threads and released ownership from main:", p);
 
    t1.join();
    t2.join();
    t3.join();
 
    std::cout << "All threads completed, the last one deleted Derived.\n";
}

Mögliche Ausgabe

Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
	get() = 0x118ac30, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
	get() = 0, use_count() = 0
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 5
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 4
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.

[edit] Beispiel

#include <iostream>
#include <memory>
 
struct MyObj
{
    MyObj() { std::cout << "MyObj constructed\n"; }
 
    ~MyObj() { std::cout << "MyObj destructed\n"; }
};
 
struct Container : std::enable_shared_from_this<Container> // note: public inheritance
{
    std::shared_ptr<MyObj> memberObj;
 
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
 
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // Use an alias shared ptr for 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("Creating shared container");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Creating member");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Creating another shared container");
    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("Copying alias obj");
    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("Resetting cont2");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Resetting myobj2");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Resetting cont");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

Ausgabe

Creating shared container
cont.use_count() = 1
cont->memberObj.use_count() = 0
 
Creating member
MyObj constructed
cont.use_count() = 1
cont->memberObj.use_count() = 1
 
Creating another shared container
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
 
Copying alias obj
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
 
Resetting cont2
myobj1.use_count() = 3
myobj2.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
 
Resetting myobj2
myobj1.use_count() = 2
cont.use_count() = 2
cont->memberObj.use_count() = 1
 
Resetting cont
myobj1.use_count() = 1
cont.use_count() = 0
MyObj destructed

[edit] Siehe auch

intelligenter Zeiger mit semantisch eindeutigem Objektbesitz
(Klassen-Template) [edit]
(C++11)
schwache Referenz auf ein von std::shared_ptr verwaltetes Objekt
(Klassen-Template) [edit]