Wenn eine Funktion eine shared_ptr
(von Boost oder C ++ 11 STL) benötigt, übergeben Sie sie:
durch konstante Referenz:
void foo(const shared_ptr<T>& p)
oder nach Wert :
void foo(shared_ptr<T> p)
?
Ich würde die erste Methode bevorzugen, weil ich vermute, dass sie schneller sein würde. Aber lohnt sich das wirklich oder gibt es zusätzliche Probleme?
Könnten Sie bitte die Gründe für Ihre Wahl angeben oder, falls dies der Fall ist, warum Sie denken, dass dies keine Rolle spielt.
c++
c++11
boost
shared-ptr
Danvil
quelle
quelle
shared_ptr
und ich kann es ändern, wenn ich will.", Während die Wertversion sagt: "Ich werde Ihre kopierenshared_ptr
, also werden Sie es nie erfahren, solange ich sie ändern kann." ) Ein const-reference-Parameter ist die eigentliche Lösung, die besagt: "Ich werde einigeshared_ptr
shared_ptr
Klassenmitglieds interessieren . Machst du es mit const-refs?Antworten:
Diese Frage wurde von Scott, Andrei und Herb während der Ask Us Anything- Sitzung bei C ++ and Beyond 2011 diskutiert und beantwortet . Achten Sie ab 4:34 auf
shared_ptr
Leistung und Korrektheit .Kurz gesagt, es gibt keinen Grund, den Wert zu übergeben, es sei denn, das Ziel besteht darin, das Eigentum an einem Objekt zu teilen (z. B. zwischen verschiedenen Datenstrukturen oder zwischen verschiedenen Threads).
Es sei denn, Sie können es verschieben, wie von Scott Meyers im oben verlinkten Diskussionsvideo erläutert, aber dies hängt mit der tatsächlichen Version von C ++ zusammen, die Sie verwenden können.
Ein wichtiges Update dieser Diskussion wurde während des interaktiven Panels der GoingNative 2012- Konferenz veröffentlicht : Fragen Sie uns alles! Das ist sehenswert, besonders ab 22:50 Uhr .
quelle
Value*
ist kurz und lesbar, aber es ist schlecht, also ist mein Code jetzt vollconst shared_ptr<Value>&
und es ist deutlich weniger lesbar und nur ... weniger aufgeräumt. Was früher war,void Function(Value* v1, Value* v2, Value* v3)
ist jetztvoid Function(const shared_ptr<Value>& v1, const shared_ptr<Value>& v2, const shared_ptr<Value>& v3)
und die Leute sind damit einverstanden?class Value {...}; using ValuePtr = std::shared_ptr<Value>;
Dann wird Ihre Funktion einfacher:void Function(const ValuePtr& v1, const ValuePtr& v2, const ValuePtr& v3)
und Sie erhalten maximale Leistung. Deshalb verwenden Sie C ++, nicht wahr? :)Hier ist Herb Sutters Einstellung
quelle
Persönlich würde ich eine
const
Referenz verwenden. Es ist nicht erforderlich, den Referenzzähler zu erhöhen, um ihn für einen Funktionsaufruf erneut zu verringern.quelle
shared_ptr
ist der einzige mögliche Nachteil, wenn keine Referenz angegeben wird, ein geringfügiger Leistungsverlust. Hier gibt es zwei Ursachen. a) Die Zeiger-Aliasing-Funktion bedeutet, dass Zeiger Daten enthalten und ein Zähler (möglicherweise 2 für schwache Refs) kopiert wird, sodass das Kopieren der Datenrunde etwas teurer ist. b) Die Atomreferenzzählung ist etwas langsamer als der alte Inkrementierungs- / Dekrementierungscode, wird jedoch benötigt, um threadsicher zu sein. Darüber hinaus sind die beiden Methoden für die meisten Absichten und Zwecke gleich.Als
const
Referenz übergeben, ist es schneller. Wenn Sie es aufbewahren müssen, sagen wir in einem Behälter, die ref. Die Anzahl wird durch den Kopiervorgang automatisch erhöht.quelle
Ich lief unter dem Code, einmal mit
foo
der Einnahmeshared_ptr
vonconst&
und wieder mitfoo
der Einnahmeshared_ptr
von Wert.Mit VS2015, x86 Release Build, auf meinem Intel Core 2 Quad (2,4 GHz) Prozessor
Die Version nach Wert war eine Größenordnung langsamer.
Wenn Sie eine Funktion synchron vom aktuellen Thread aufrufen, bevorzugen Sie die
const&
Version.quelle
foo()
Funktion nicht einmal sollte einen gemeinsamen Zeiger in erster Linie akzeptieren , weil es dieses Objekt nicht verwendet hat: es sollte eine akzeptierenint&
und zu tunp = ++x;
, ruftfoo(*p);
ausmain()
. Eine Funktion akzeptiert ein Smart-Pointer-Objekt, wenn es etwas damit tun muss. In den meisten Fällen müssen Sie es (std::move()
) an einen anderen Ort verschieben, sodass ein By-Value-Parameter keine Kosten verursacht.Seit C ++ 11 sollten Sie es öfter als gedacht als Wert über const & nehmen .
Wenn Sie std :: shared_ptr (anstelle des zugrunde liegenden Typs T) verwenden, tun Sie dies, weil Sie etwas damit tun möchten.
Wenn Sie es irgendwo kopieren möchten , ist es sinnvoller, es per Kopie zu nehmen und std :: intern zu verschieben, als es durch const & zu nehmen und später zu kopieren. Dies liegt daran, dass Sie dem Aufrufer die Möglichkeit geben, beim Aufrufen Ihrer Funktion wiederum std :: move_ptr zu verschieben, wodurch Sie sich eine Reihe von Inkrementierungs- und Dekrementierungsoperationen ersparen. Oder nicht. Das heißt, der Aufrufer der Funktion kann entscheiden, ob er nach dem Aufrufen der Funktion std :: shared_ptr benötigt oder nicht, und abhängig davon, ob er sich bewegt oder nicht. Dies ist nicht erreichbar, wenn Sie an const & vorbeikommen, und daher ist es dann vorzugsweise, es als Wert zu nehmen.
Natürlich, wenn der Aufrufer sein shared_ptr länger benötigt (kann es also nicht std :: move) und Sie keine einfache Kopie in der Funktion erstellen möchten (sagen Sie, Sie möchten einen schwachen Zeiger oder Sie möchten nur manchmal um es zu kopieren, abhängig von einer Bedingung), dann ist ein const & möglicherweise immer noch vorzuziehen.
Zum Beispiel sollten Sie tun
Über
Denn in diesem Fall erstellen Sie immer intern eine Kopie
quelle
Da ich die Zeitkosten für den Shared_copy-Kopiervorgang nicht kenne, bei denen das atomare Inkrementieren und Dekrementieren erfolgt, litt ich unter einem viel höheren CPU-Auslastungsproblem. Ich hätte nie gedacht, dass atomares Inkrementieren und Dekrementieren so viel Kosten verursachen könnte.
Nach meinem Testergebnis dauert das Inkrementieren und Dekrementieren von int32-Atomen zwei- oder 40-mal länger als das Inkrementieren und Dekrementieren von nichtatomaren Atomen. Ich habe es auf 3GHz Core i7 mit Windows 8.1 bekommen. Das erstere Ergebnis kommt heraus, wenn kein Konflikt auftritt, das letztere, wenn eine hohe Wahrscheinlichkeit eines Konflikts auftritt. Ich denke daran, dass atomare Operationen endlich hardwarebasierte Sperren sind. Schloss ist Schloss. Schlechte Leistung, wenn Konflikte auftreten.
In diesem Fall verwende ich immer byref (const shared_ptr &) als byval (shared_ptr).
quelle
Es gab kürzlich einen Blog-Beitrag: https://medium.com/@vgasparyan1995/pass-by-value-vs-pass-by-reference-to-const-c-f8944171e3ce
Die Antwort darauf lautet also: Gehen Sie (fast) nie vorbei
const shared_ptr<T>&
.Übergeben Sie stattdessen einfach die zugrunde liegende Klasse.
Grundsätzlich sind die einzigen vernünftigen Parametertypen:
shared_ptr<T>
- Ändern und übernehmen Sie das Eigentumshared_ptr<const T>
- Nicht ändern, Eigentum übernehmenT&
- Ändern, kein Eigentumconst T&
- Nicht ändern, kein EigentumT
- Nicht ändern, kein Eigentum, billig zu kopierenWie @accel in https://stackoverflow.com/a/26197326/1930508 hervorhob, lautet der Rat von Herb Sutter:
Aber in wie vielen Fällen sind Sie sich nicht sicher? Das ist also eine seltene Situation
quelle
Es ist bekannt, dass das Übergeben von shared_ptr als Wert Kosten verursacht und nach Möglichkeit vermieden werden sollte.
Die Kosten für die Übergabe von shared_ptr
Die meiste Zeit würde es reichen, shared_ptr als Referenz und noch besser als const-Referenz zu übergeben.
Die cpp-Kernrichtlinie enthält eine spezielle Regel für die Übergabe von shared_ptr
R.34: Nehmen Sie einen shared_ptr-Parameter, um auszudrücken, dass eine Funktion Teilbesitzer ist
Ein Beispiel dafür, wie die Übergabe von shared_ptr als Wert wirklich erforderlich ist, ist die Übergabe eines gemeinsam genutzten Objekts an einen asynchronen Angerufenen, dh der Anrufer verlässt den Gültigkeitsbereich, bevor der Angerufene seinen Job beendet. Der Angerufene muss die Lebensdauer des gemeinsam genutzten Objekts "verlängern", indem er einen share_ptr nach Wert nimmt. In diesem Fall reicht es nicht, einen Verweis auf shared_ptr zu übergeben.
Gleiches gilt für die Übergabe eines gemeinsam genutzten Objekts an einen Arbeitsthread.
quelle
shared_ptr ist nicht groß genug, und sein Konstruktor \ destructor leistet nicht genug Arbeit, um genügend Overhead von der Kopie zu haben, um sich um die Referenzübergabe und die Leistung beim Übergeben der Kopie zu kümmern.
quelle
shared_ptr<int>
Werts mehr als 100 x 86-Anweisungen benötigt (einschließlich teurerlock
ed-Anweisungen, um die Ref-Anzahl atomar zu erhöhen / zu verringern). Das Übergeben einer konstanten Referenz ist dasselbe wie das Übergeben eines Zeigers auf irgendetwas (und in diesem Beispiel im Godbolt-Compiler-Explorer verwandelt die Tail-Call-Optimierung dies in ein einfaches jmp anstelle eines Aufrufs: godbolt.org/g/TazMBU ).