Namensräume
Varianten
Aktionen

std::make_shared, std::make_shared_for_overwrite

Von cppreference.com
< cpp‎ | memory‎ | shared ptr
 
 
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... Args >
shared_ptr<T> make_shared( Args&&... args );
(1) (seit C++11)
template< class T >
shared_ptr<T> make_shared( std::size_t N );
(2) (seit C++20)
template< class T >
shared_ptr<T> make_shared();
(3) (seit C++20)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u );
(4) (seit C++20)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u );
(5) (seit C++20)
template< class T >
shared_ptr<T> make_shared_for_overwrite();
(6) (seit C++20)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N );
(7) (seit C++20)

Weist Speicher für ein Objekt zu und initialisiert das Objekt mit den übergebenen Argumenten. Gibt ein std::shared_ptr-Objekt zurück, das das neu erstellte Objekt verwaltet.

1) Das Objekt ist vom Typ T und wird konstruiert, als ob durch ::new (pv) T(std::forward<Args>(args)...), wobei pv ein void* Zeiger auf Speicher ist, der ausreicht, um ein Objekt vom Typ T zu speichern. Wenn das Objekt zerstört werden soll, wird es zerstört, als ob durch pt->~T(), wobei pt ein Zeiger auf dieses Objekt vom Typ T ist.

Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T kein Array-Typ ist.

(seit C++20)
2) Das Objekt ist vom Typ std::remove_extent_t<T>[N]. Jedes Element hat einen Standardinitialisierungswert.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T ein unbegrenzter Array-Typ ist.
3) Das Objekt ist vom Typ T. Jedes Element hat einen Standardinitialisierungswert.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T ein begrenzter Array-Typ ist.
4) Das Objekt ist vom Typ std::remove_extent_t<T>[N]. Jedes Element hat den Initialisierungswert u.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T ein unbegrenzter Array-Typ ist.
5) Das Objekt ist vom Typ T. Jedes Element hat den Initialisierungswert u.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T ein begrenzter Array-Typ ist.
6) Das Objekt ist vom Typ T.
  • Wenn T kein Array-Typ ist, wird das Objekt konstruiert, als ob durch ::new (pv) T, wobei pv ein void* Zeiger auf Speicher ist, der ausreicht, um ein Objekt vom Typ T zu speichern. Wenn das Objekt zerstört werden soll, wird es zerstört, als ob durch pt->~T(), wobei pt ein Zeiger auf dieses Objekt vom Typ T ist.
  • Wenn T ein begrenzter Array-Typ ist, ist der Initialisierungswert für jedes Element nicht spezifiziert.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T kein Array-Typ oder ein begrenzter Array-Typ ist.
7) Das Objekt ist vom Typ std::remove_extent_t<T>[N]. Der Initialisierungswert ist für jedes Element nicht spezifiziert.
Diese Überladung nimmt nur an der Überladungsauflösung teil, wenn T ein unbegrenzter Array-Typ ist.

Inhalt

Initialisieren und Zerstören von Array-Elementen

Array-Elemente vom Typ U werden in aufsteigender Reihenfolge ihrer Adressen initialisiert.

  • Wenn U kein Array-Typ ist, wird jedes Element als ob durch den folgenden Ausdruck konstruiert, wobei pv ein void* Zeiger auf Speicher ist, der ausreicht, um ein Objekt vom Typ U zu speichern
2,3) ::new (pv) U()
4,5) ::new (pv) U(u)
6,7) ::new (pv) U
  • Andernfalls werden die Elemente jeder Dimension rekursiv initialisiert. Für die nächste Dimension
  • wird U zu std::remove_extent_t<U>.
  • Für die Überladungen (4,5) wird u zum entsprechenden Element von u.

Wenn die Lebensdauer des vom zurückgegebenen std::shared_ptr verwalteten Objekts endet, oder wenn die Initialisierung eines Array-Elements eine Ausnahme auslöst, werden die initialisierten Elemente in umgekehrter Reihenfolge ihrer ursprünglichen Konstruktion zerstört.

Für jedes zu zerstörende Array-Element vom Nicht-Array-Typ U wird es zerstört, als ob durch pu->~U(), wobei pu ein Zeiger auf dieses Array-Element vom Typ U ist.

(seit C++20)

[edit] Parameter

args - Liste der Argumente, mit denen ein Objekt vom Typ T konstruiert werden soll
N - Array-Größe, die verwendet werden soll
u - der Initialisierungswert zur Initialisierung jedes Elements des Arrays

[edit] Rückgabewert

std::shared_ptr auf ein Objekt vom Typ T oder std::remove_extent_t<T>[N], wenn T ein unbegrenzter Array-Typ ist(seit C++20).

Für den zurückgegebenen std::shared_ptr r gibt r.get() einen Nicht-Null-Zeiger zurück und r.use_count() gibt 1 zurück.

[edit] Ausnahmen

Kann std::bad_alloc oder jede Ausnahme auslösen, die vom Konstruktor von T ausgelöst wird. Wenn eine Ausnahme ausgelöst wird, haben die Funktionen keine Auswirkungen. Wenn während der Konstruktion des Arrays eine Ausnahme ausgelöst wird, werden bereits initialisierte Elemente in umgekehrter Reihenfolge zerstört.(seit C++20)

[edit] Hinweise

Diese Funktionen weisen typischerweise mehr Speicher zu als sizeof(T), um interne Verwaltungsstrukturen wie Referenzzähler zu ermöglichen.

Diese Funktionen können als Alternative zu std::shared_ptr<T>(new T(args...)) verwendet werden. Die Kompromisse sind

  • std::shared_ptr<T>(new T(args...)) führt mindestens zwei Zuweisungen durch (eine für das Objekt T und eine für den Steuerblock des Shared Pointers), während std::make_shared<T> typischerweise nur eine Zuweisung durchführt (der Standard empfiehlt dies, schreibt es aber nicht vor; alle bekannten Implementierungen tun dies).
  • Wenn sich ein std::weak_ptr auf den von std::make_shared erstellten Steuerblock bezieht, nachdem die Lebensdauer aller gemeinsamen Besitzer beendet ist, bleibt der von T belegte Speicher erhalten, bis auch alle schwachen Besitzer zerstört sind, was unerwünscht sein kann, wenn sizeof(T) groß ist.
  • std::shared_ptr<T>(new T(args...)) kann einen nicht-öffentlichen Konstruktor von T aufrufen, wenn er in einem Kontext ausgeführt wird, in dem er zugänglich ist, während std::make_shared öffentlichen Zugriff auf den ausgewählten Konstruktor benötigt.
  • Im Gegensatz zu den std::shared_ptr-Konstruktoren erlaubt std::make_shared keinen benutzerdefinierten Deleter.
  • std::make_shared verwendet ::new, daher wird es, wenn durch eine klassenspezifische operator new ein spezielles Verhalten eingerichtet wurde, von std::shared_ptr<T>(new T(args...)) abweichen.
(bis C++20)
  • Code wie f(std::shared_ptr<int>(new int(42)), g()) kann zu einem Speicherleck führen, wenn g nach new int(42) aufgerufen wird und eine Ausnahme auslöst, während f(std::make_shared<int>(42), g()) sicher ist, da zwei Funktionsaufrufe nie verschachtelt werden.
(bis C++17)

Ein Konstruktor, der shared_from_this ermöglicht mit einem Zeiger ptr vom Typ U*, bedeutet, dass er feststellt, ob U eine eindeutige und zugängliche(seit C++17) Basisklasse hat, die eine Spezialisierung von std::enable_shared_from_this ist, und wenn ja, evaluiert der Konstruktor if (ptr != nullptr && ptr->weak_this .expired())
    ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>
        (*this, const_cast<std::remove_cv_t<U>*>(ptr));
.

Die Zuweisung an weak_this ist nicht atomar und steht in Konflikt mit jedem potenziell gleichzeitigen Zugriff auf dasselbe Objekt. Dies stellt sicher, dass zukünftige Aufrufe von shared_from_this() den durch diesen Rohzeiger-Konstruktor erstellten gemeinsamen Besitz mit dem std::shared_ptr teilen würden.

Der Test ptr->weak_this .expired() im obigen Code stellt sicher, dass weak_this nicht neu zugewiesen wird, wenn er bereits einen Besitzer anzeigt. Dieser Test ist ab C++17 erforderlich.

Feature-Test-Makro Wert Std Feature
__cpp_lib_shared_ptr_arrays 201707L (C++20) Array-Unterstützung für std::make_shared; Überladungen (2-5)
__cpp_lib_smart_ptr_for_overwrite 202002L (C++20) Erstellung von Smart Pointern mit Standardinitialisierung (std::allocate_shared_for_overwrite, std::make_shared_for_overwrite, std::make_unique_for_overwrite); Überladungen (6,7)

[edit] Beispiel

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
 
struct C
{
    // constructors needed (until C++20)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
 
int main()
{
    // using “auto” for the type of “sp1”
    auto sp1 = std::make_shared<C>(1); // overload (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
 
    // being explicit with the type of “sp2”
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // overload (1)
    static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
    static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
    std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
 
    // shared_ptr to a value-initialized float[64]; overload (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
 
    // shared_ptr to a value-initialized long[5][3][4]; overload (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
 
    // shared_ptr to a value-initialized short[128]; overload (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
 
    // shared_ptr to a value-initialized int[7][6][5]; overload (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
 
    // shared_ptr to a double[256], where each element is 2.0; overload (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
 
    // shared_ptr to a double[7][2], where each double[2]
    // element is {3.0, 4.0}; overload (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
 
    // shared_ptr to a float[512], where each element is 1.0; overload (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
 
    // shared_ptr to a double[6][2], where each double[2] element
    // is {1.0, 2.0}; overload (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (5):
    std::shared_ptr<std::vector<int>[4]> spC =
        std::make_shared<std::vector<int>[4]>({5, 6});
}

Ausgabe

sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }

[edit] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
LWG 4024 C++20 war unklar, wie die in
std::make_shared_for_overwrite konstruierten Objekte zerstört werden
wurde klargestellt

[edit] Siehe auch

erstellt einen neuen shared_ptr
(public member function) [edit]
erstellt einen geteilten Zeiger, der ein neues Objekt verwaltet, das mithilfe eines Allokators zugewiesen wurde
(Funktionsschablone) [bearbeiten]
ermöglicht einem Objekt, einen auf sich selbst verweisenden shared_ptr zu erstellen
(Klassen-Template) [edit]
Erstellt einen unique Pointer, der ein neues Objekt verwaltet
(Funktionsschablone) [bearbeiten]
Allokationsfunktionen
(Funktion) [bearbeiten]