2014-07-16 24 views
25

Verwendung Ich bin interessiert, wenn diese beiden Codezeilen sind die gleichen:Was passiert, wenn make_shared

shared_ptr<int> sp(new int(1)); // double allocation? 
shared_ptr<int> sp(make_shared<int>(1)); // just one allocation? 

Wenn dies der Fall ist, könnte jemand bitte erklären, warum ist es nur eine Zuordnung in der zweiten Zeile?

+4

Um klar zu sein, es ist nicht eine doppelte Zuweisung der 'Int'. Es sind nur zwei separate Zuweisungen: eine für das Objekt "int" und eine weitere für den Steuerblock "shared_ptr". Die zweite Zeile ist nur eine einzige Zuweisung von "int" und dem Steuerblock auf einmal. –

+2

siehe Punkt 2 hier: http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ – perreal

+1

Im zweiten Fall weist 'make_shared' sowohl den' int' als auch den Kontrollblock zu und ist daher frei, beides auf einmal zuzuweisen. Im ersten Fall ordnen Sie das "int" zu und der Konstruktor von "shared_ptr" weist den Kontrollblock zu und es gibt keine Möglichkeit, die Zuweisungen zu vereinigen. – nwp

Antwort

31

Der erste Fall ist eine Doppelzuweisung nicht ausgeführt werden, führt es zwei Zuführungen, eine für das verwaltete Objekt und einen für den Steuerblock der shared_ptr.

Für den zweiten Fall hat cppreference eine gute Erklärung dafür, warum std::make_sharedin der Regel führt nur eine Speicherzuweisung es sagt (Hervorhebung von mir vorwärts gehen):

Diese Funktion ordnet typischerweise Speicher für die T Objekt und für die shared_ptr Steuerblock mit einer einzigen Speicherbelegung (es ist eine unverbindliche Anforderung in der Norm). Im Gegensatz dazu führt die Deklaration std :: shared_ptr p (neues T (Args ...)) mindestens zwei Speicher Zuordnungen durch, die unnötigen Overhead verursachen können.

und von std::shared_ptr Abschnitt heißt es:

Wenn Shared_ptr durch den Aufruf std erstellt :: make_shared oder std :: allocate_shared, den Speicher für sowohl den Steuerblock und die verwaltete Objekt erstellt wird, mit einer einzigen Zuteilung. Das verwaltete Objekt wird direkt in einem Datenelement des Steuerblocks erstellt. Wenn shared_ptr über einen der shared_ptr-Konstruktoren erstellt wird, müssen das verwaltete Objekt und der Steuerblock separat zugewiesen werden. In diesem Fall speichert der Steuerblock einen Zeiger auf das verwaltete Objekt.

Diese make_shared Beschreibung ist mit dem C++11 draft standard konsistent, die in der Schöpfung Abschnitt 20.7.2.2.6Shared_ptr sagt

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); 
template<class T, class A, class... Args> 
    shared_ptr<T> allocate_shared(const A& a, Args&&... args); 

[...]

Bemerkungen: Implementations nicht mehr als ein durchführen sollten Speicher Zuordnung. [Hinweis: Dies bietet eine Effizienz, die einem intrusiven Smart-Pointer entspricht. -end note]

[Hinweis: Diese Funktionen weisen normalerweise mehr Speicher als sizeof (T) zu, um interne Buchhaltungsstrukturen wie die Referenzzählungen zu ermöglichen.-Ende note]

Herb Sutter hat eine ausführlichere Erläuterung der Vorteile der Verwendung von make_shared in GotW #89 Solution: Smart Pointers und einige Vorteile betont:

  • Es reduziert Kopf Zuordnung
  • Es verbessert die Lokalität.
  • Vermeidet eine explizite neue.
  • Vermeidet ein Ausnahmesicherheitsproblem.

Seien Sie sich bewusst, dass, wenn std::weak_ptrusing make_shared has some disadvantages verwenden.

4

Erklärung von cppreference std::shared_ptr in Implementation notes Abschnitt

In einer typischen Implementierung std :: shared_ptr hält nur zwei Zeiger:

  1. ein Zeiger auf das verwaltete Objekt
  2. ein Zeigerblock zu steuern,

Wenn shared_ptr durch Aufruf erstellt wird std :: make_shared oder std :: allocate_shared, der Speicher für den Steuerblock und das verwaltete Objekt wird mit einer einzigen Zuweisung erstellt. Das verwaltete Objekt wird direkt in einem Datenelement des Steuerblocks erstellt. Wenn shared_ptr über einen der shared_ptr-Konstruktoren erstellt wird, müssen das verwaltete Objekt und der Steuerblock separat zugewiesen werden. In diesem Fall speichert der Steuerblock einen Zeiger auf das verwaltete Objekt.

0

Es gibt auch eine mögliche subtile Fehler: in sp(new int) Sie einen int Faust zuteilen (dessen Zeiger gegeben sp), als sp selbst einen Steuerblock zuzuweisen hat (wird die Zähler und die deleter enthalten).

Nun, wenn diese letzte Zuweisung sp fehlschlägt (wenig Speicher), sind Sie mit einem Heap reserviert Int, dessen Zeiger von niemandem gehalten wird, und somit unmöglich zu löschen. (Speicherleck).

Verwandte Themen