Unterschiede zwischen std :: make_unique und std :: unique_ptr mit new

130

Hat std::make_uniqueirgendwelche Effizienzvorteile wie std::make_shared?

Im Vergleich zum manuellen Erstellen std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
NFRCR
quelle
Hat make_sharedes eine Effizienz, nur den langen Handcode zu schreiben?
Ed Heal
9
@EdHeal Möglicherweise, weil make_sharedsowohl der Speicherplatz für das Objekt als auch der Speicherplatz für den Steuerblock in einer einzigen Zuordnung zugeordnet werden können. Die Kosten hierfür sind, dass das Objekt nicht getrennt vom Steuerblock freigegeben werden kann. Wenn Sie also weak_ptrviel verwenden, wird möglicherweise mehr Speicher benötigt.
Bames53
Vielleicht ist dies ein guter Ausgangspunkt stackoverflow.com/questions/9302296/…
Ed Heal

Antworten:

140

Die Motivation dahinter make_uniqueist in erster Linie zweierlei:

  • make_uniqueist sicher für das Erstellen von Provisorien, während Sie bei expliziter Verwendung newdie Regel beachten müssen, unbenannte Provisorien nicht zu verwenden.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • Das Hinzufügen von make_uniquefinally bedeutet, dass wir den Leuten sagen können, dass sie "niemals" newanstelle der vorherigen Regel "niemals" verwenden sollen, newaußer wenn Sie ein unique_ptr" machen ".

Es gibt auch einen dritten Grund:

  • make_uniqueerfordert keine redundante Typverwendung. unique_ptr<T>(new T())->make_unique<T>()

Keiner der Gründe beinhaltet die Verbesserung der Laufzeiteffizienz wie bei der Verwendung make_shared(aufgrund der Vermeidung einer zweiten Zuweisung auf Kosten einer möglicherweise höheren Spitzenspeicherauslastung).

* Es wird erwartet, dass C ++ 17 eine Regeländerung enthält, die bedeutet, dass dies nicht länger unsicher ist. Siehe C ++ - Ausschussdokumente P0400R0 und P0145R3 .

bames53
quelle
Es wäre sinnvoller zu sagen std::unique_ptrund std::shared_ptrdeshalb können wir den Leuten sagen, sie sollen "niemals benutzen new".
Timothy Shields
2
@ TimothyShields Ja, das meine ich. Es ist nur so, dass wir in C ++ 11 haben make_sharedund so make_uniqueist das letzte Stück, das vorher fehlte.
Bames53
1
Wie könnten Sie kurz erwähnen oder auf den Grund verweisen, warum Sie keine unbenannten Provisorien verwenden?
Dan Nissenbaum
14
Eigentlich aus stackoverflow.com/a/19472607/368896 , ich habe es ... Von dieser Antwort, sollten Sie den folgenden Funktionsaufruf f: f(unique_ptr<T>(new T), function_that_can_throw());- die Antwort zu zitieren: Die Compiler Aufruf erlaubt ist (in dieser Reihenfolge): new T, function_that_can_throw(), unique_ptr<T>(...). Offensichtlich, wenn function_that_can_throwtatsächlich wirft, dann lecken Sie. make_uniqueverhindert diesen Fall. Also ist meine Frage beantwortet.
Dan Nissenbaum
3
Ein Grund, warum ich einmal std :: unique_ptr <T> (neues T ()) verwenden musste, war, dass der Konstruktor von T privat war. Selbst wenn sich der Aufruf von std :: make_unique in einer öffentlichen Factory-Methode der Klasse T befand, wurde er nicht kompiliert, da eine der zugrunde liegenden Methoden von std :: make_unique nicht auf den privaten Konstruktor zugreifen konnte. Ich wollte diese Methode nicht zu einem Freund machen, weil ich mich nicht auf die Implementierung von std :: make_unique verlassen wollte. Die einzige Lösung bestand darin, in meiner Factory-Methode der Klasse T new aufzurufen und sie dann in ein std :: unique_ptr <T> zu verpacken.
Patrick
14

std::make_uniqueund std::make_sharedgibt es aus zwei Gründen:

  1. Damit Sie die Argumente des Vorlagentyps nicht explizit auflisten müssen.
  2. Zusätzliche Ausnahmesicherheit gegenüber Verwendung std::unique_ptroder std::shared_ptrKonstruktoren. (Siehe den Abschnitt Notizen hier .)

Es geht nicht wirklich um Laufzeiteffizienz. Es gibt ein bisschen über den Steuerblock und die TZuweisung auf einmal, aber ich denke, das ist mehr ein Bonus und weniger eine Motivation für das Vorhandensein dieser Funktionen.

Timothy Shields
quelle
Sie sind auch für Ausnahmesicherheit da.
0x499602D2
@ 0x499602D2 Und das, gute Ergänzung. Diese Seite spricht darüber.
Timothy Shields
Für zukünftige Leser erlaubt C ++ 17 keine Verschachtelung von Funktionsargumenten, sodass das Argument für die Ausnahmesicherheit nicht mehr gilt. Die Speicherzuweisung für zwei parallele Speicher stellt std::make_sharedsicher, dass mindestens einer von ihnen in den Smart Pointer eingeschlossen wird, bevor die andere Speicherzuweisung erfolgt, sodass keine Lecks auftreten.
MathBunny
7

Ein Grund, warum Sie std::unique_ptr(new A())oder std::shared_ptr(new A())direkt verwenden müssten, std::make_*()ist, dass Sie nicht auf den Konstruktor der Klasse Aaußerhalb des aktuellen Bereichs zugreifen können .

Volodymyr Lashko
quelle
0

Funktionsaufruf berücksichtigen

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Angenommen, new ist A()erfolgreich, new B()löst jedoch eine Ausnahme aus: Sie fangen sie ab, um die normale Ausführung Ihres Programms fortzusetzen. Leider verlangt der C ++ - Standard nicht, dass Objekt A zerstört und sein Speicher freigegeben wird: Der Speicher leckt stillschweigend und es gibt keine Möglichkeit, ihn zu bereinigen. Wenn Sie A und B einwickeln, können std::make_uniquesSie sicher sein, dass das Leck nicht auftritt:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

Der Punkt hier ist, dass std::make_unique<A>und std::make_unique<B>sind jetzt temporäre Objekte, und die Bereinigung von temporären Objekten ist im C ++ - Standard korrekt angegeben: Ihre Destruktoren werden ausgelöst und der Speicher freigegeben. Wenn Sie können, ziehen Sie es immer vor, Objekte mit std::make_uniqueund zuzuweisen std::make_shared.

Dhanya Gopinath
quelle