Soweit ich weiß, wurde C ++ 14 eingeführt std::make_unique
, da dies aufgrund der nicht angegebenen Reihenfolge der Parameterauswertung unsicher war:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Erläuterung: Wenn die Auswertung zuerst den Speicher für den Rohzeiger zuweist, dann aufruft g()
und vor der std::unique_ptr
Konstruktion eine Ausnahme ausgelöst wird , geht der Speicher verloren.)
Berufung std::make_unique
war ein Weg , um den Anruf zu beschränken, damit die Dinge sicher:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Seitdem hat C ++ 17 die Auswertungsreihenfolge geklärt und Syntax A auch sicher gemacht. Hier ist meine Frage: Gibt es noch einen Grund, den Konstruktor von std::make_unique
over std::unique_ptr
in C ++ 17 zu verwenden? Können Sie einige Beispiele nennen?
Der einzige Grund, den ich mir vorstellen kann, ist, dass MyClass
nur einmal getippt werden kann (vorausgesetzt, Sie müssen sich nicht auf Polymorphismus verlassen std::unique_ptr<Base>(new Derived(param))
). Dies scheint jedoch ein ziemlich schwacher Grund zu sein, insbesondere wenn std::make_unique
es nicht erlaubt ist, einen Deleter anzugeben, während dies std::unique_ptr
der Konstruktor tut.
Und um ganz klar zu sein, ich befürworte nicht das Entfernen std::make_unique
aus der Standardbibliothek (es ist zumindest aus Gründen der Abwärtskompatibilität sinnvoll, dies beizubehalten), sondern frage mich, ob es noch Situationen gibt, in denen dies stark bevorzugt wirdstd::unique_ptr
quelle
std::unique_ptr
? Es ist kein Argument gegenmake_unique
std::make_unique
überhaupt keinen gäbe, denke ich nicht, dass dies Grund genug wäre, ihn der STL hinzuzufügen, insbesondere wenn es sich um eine Syntax handelt, die weniger aussagekräftig ist als die Verwendung des Konstruktors, nicht mehrAntworten:
Sie haben Recht, dass der Hauptgrund entfernt wurde. Es gibt immer noch keine neuen Richtlinien und es gibt weniger Gründe für die Eingabe (Sie müssen den Typ nicht wiederholen oder das Wort verwenden
new
). Zugegeben, das sind keine starken Argumente, aber ich mag es wirklich, nichtnew
in meinem Code zu sehen.Vergessen Sie auch nicht die Konsistenz. Sie sollten es unbedingt verwenden,
make_shared
damitmake_unique
es natürlich ist und zum Muster passt. Es ist dann trivial,std::make_unique<MyClass>(param)
zustd::make_shared<MyClass>(param)
(oder umgekehrt) zu wechseln , wo die Syntax A viel mehr Umschreiben erfordert.quelle
new
sehe, muss ich innehalten und überlegen : Wie lange wird dieser Zeiger noch leben? Habe ich richtig damit umgegangen? Wenn es eine Ausnahme gibt, wird alles korrekt bereinigt? Ich möchte mir diese Fragen nicht stellen und meine Zeit damit verschwenden, und wenn ich sie nicht benutzenew
, muss ich diese Fragen nicht stellen.new
. Wäre das nicht wunderbar?std::make_shared
- stellen Sie sich einen Fall vor, in dem das zugewiesene Objekt groß ist und vielestd::weak_ptr
-s darauf hinweisen: Es ist besser, das Objekt löschen zu lassen, sobald das letzte freigegeben wurde Zeiger ist zerstört und leben mit nur einem kleinen gemeinsamen Bereich.std::make_shared
stackoverflow.com/a/20895705/8414561, bei dem der Speicher, in dem das Objekt gespeichert wurde, erst freigegeben werden kann, wenn der letzte Speicher nicht mehr vorhandenstd::weak_ptr
ist (auch wenn allestd::shared_ptr
-s darauf zeigen (und) folglich wurde das Objekt selbst) bereits zerstört).make_unique
unterscheidet sichT
vonT[]
undT[N]
,unique_ptr(new ...)
nicht.Sie können leicht undefiniertes Verhalten erhalten, indem Sie einen Zeiger
new[]
an a übergebenunique_ptr<T>
oder einen Zeigernew
an a übergebenunique_ptr<T[]>
.quelle
Der Grund ist, kürzeren Code ohne Duplikate zu haben. Vergleichen Sie
Sie sparen
MyClass
,new
und Klammern. Es kostet nur ein Zeichen mehr in make im Vergleich zu ptr .quelle
MyClass
, aber ich habe mich gefragt, ob es einen stärkeren Grund gibt, es zu verwenden<MyClass>
Teil in der ersten Variante zu eliminieren .std::unique_ptr
nicht zulässig. Es hat mit Unterscheidungstd::unique_ptr<T>
undstd::unique_ptr<T[]>
Jede Verwendung von
new
muss besonders sorgfältig auf lebenslange Korrektheit geprüft werden. wird es gelöscht? Nur einmal?Jede Verwendung von
make_unique
nicht für diese zusätzlichen Eigenschaften; Solange das besitzende Objekt eine "korrekte" Lebensdauer hat, wird der eindeutige Zeiger rekursiv "korrekt".Nun ist es wahr, dass
unique_ptr<Foo>(new Foo())
in jeder Hinsicht 1 bis identisch istmake_unique<Foo>()
; Es erfordert lediglich eine einfachere "Grep Ihren Quellcode für alle Verwendungszwecke, um sienew
zu prüfen".Ich bin eigentlich eine Lüge im allgemeinen Fall. Perfekte Weiterleitung ist nicht perfekt,
{}
Standard-Init, Arrays sind alle Ausnahmen.quelle
unique_ptr<Foo>(new Foo)
ist das nicht ganz identisch mitmake_unique<Foo>()
... Letzteres tut esnew Foo()
aber sonst ja.std::unique_ptr
dieser nicht zulässig ist. Wenn es um die Unterscheidung gehtstd::unique_ptr<T>
undstd::unique_ptr<T[]>
Das ist wirklich nicht gut genug. Es ist keine sehr robuste Praxis, sich auf eine kürzlich eingeführte technische Klausel als Sicherheitsgarantie zu stützen:
new
anderer Stelle fördern , z. B. indem Sie Ihr Beispiel kopieren.new
andere Stelle verschieben (ok, zugegeben, keine große Chance dafür).Im Allgemeinen ist es eine gute Idee, dass Ihr Code angemessen / robust / eindeutig gültig ist, ohne auf Sprachkenntnisse zurückzugreifen und kleinere oder undurchsichtige technische Klauseln im Standard nachzuschlagen.
(Dies ist im Wesentlichen das gleiche Argument, das ich hier über die Reihenfolge der Tupelzerstörung vorgebracht habe.)
quelle
Betrachten Sie die void-Funktion (std :: unique_ptr (neues A ()), std :: unique_ptr (neues B ())) {...}
Angenommen, neues A () ist erfolgreich, aber neues B () löst eine Ausnahme aus: Sie fangen es 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 in std :: make_uniques einschließen, können Sie sicher sein, dass das Leck nicht auftritt:
void function (std :: make_unique (), std :: make_unique ()) {...} Hier geht es darum, dass std :: make_unique und std :: make_unique jetzt temporäre Objekte sind und die Bereinigung temporärer Objekte in korrekt angegeben ist der C ++ - Standard: Ihre Destruktoren werden ausgelöst und der Speicher freigegeben. Wenn Sie können, ziehen Sie es immer vor, Objekte mit std :: make_unique und std :: make_shared zuzuweisen.
quelle