In herkömmlichem C ++ ist die Übergabe von Werten an Funktionen und Methoden für große Objekte langsam und wird im Allgemeinen verpönt. Stattdessen neigen C ++ - Programmierer dazu, Referenzen weiterzugeben, was schneller ist, aber alle möglichen komplizierten Fragen zum Besitz und insbesondere zur Speicherverwaltung aufwirft (falls das Objekt Heap-zugewiesen ist).
Jetzt haben wir in C ++ 11 Rvalue-Referenzen und Verschiebungskonstruktoren, was bedeutet, dass es möglich ist, ein großes Objekt (wie ein Objekt std::vector
) zu implementieren , das kostengünstig als Wert in eine Funktion und aus einer Funktion übergeben werden kann.
Bedeutet dies also, dass der Standardwert für Instanzen von Typen wie std::vector
und als Wert übergeben werden sollte std::string
? Was ist mit benutzerdefinierten Objekten? Was ist die neue Best Practice?
quelle
pass by reference ... which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)
. Ich verstehe nicht, wie kompliziert oder problematisch es für das Eigentum ist? Kann ich etwas verpasst haben?const std::string&
und nicht als Kopie verwendet hat. Der erste Thread wurde dann beendet ...Antworten:
Dies ist eine vernünftige Standardeinstellung, wenn Sie eine Kopie im Text erstellen müssen. Dies ist, was Dave Abrahams befürwortet :
Im Code bedeutet dies, dass Sie Folgendes nicht tun:
aber mach das:
Das hat den Vorteil, dass der Anrufer
foo
wie folgt nutzen kann:und es wird nur minimale Arbeit geleistet. Sie benötigen zwei Überladungen, um dasselbe mit Referenzen zu tun,
void foo(T const&);
undvoid foo(T&&);
.In diesem Sinne habe ich jetzt meine geschätzten Konstruktoren als solche geschrieben:
Andernfalls
const
ist es vernünftig, auf still zu verweisen .quelle
SomeProperty p;
for (auto x: vec) { x.foo(p); }
bleibt : passt beispielsweise nicht. Außerdem haben Verschiebungskonstruktoren Kosten (je größer das Objekt, desto höher die Kosten), während sieconst&
im Wesentlichen kostenlos sind.std::vector
mit einer Million Elementen dasselbe wie das Verschieben eines mit fünf Elementen, da nur der Zeiger auf das Array auf dem Heap verschoben wird, nicht jedes Objekt im Vektor. Es ist also eigentlich kein so großes Problem.std::move
überall ist ..const&
, das mich ein paar Mal gestolpert hat.void foo(const T&); int main() { S s; foo(s); }
. Dies kann kompiliert werden, obwohl die Typen unterschiedlich sind, wenn es einen T-Konstruktor gibt, der ein S als Argument verwendet. Dies kann langsam sein, da möglicherweise ein großes T-Objekt erstellt wird. Sie könnten denken, Sie würden eine Referenz übergeben, ohne sie zu kopieren, aber Sie könnten es sein. Siehe diese Antwort auf eine Frage, die ich nach mehr gefragt habe . Grundsätzlich&
bindet normalerweise nur an lWerte, aber es gibt eine Ausnahme fürrvalue
. Es gibt Alternativen.In fast allen Fällen sollte Ihre Semantik entweder wie folgt sein:
Alle anderen Unterschriften sollten nur sparsam und mit guter Begründung verwendet werden. Der Compiler wird diese nun so gut wie immer auf die effizienteste Weise herausarbeiten. Sie können einfach mit dem Schreiben Ihres Codes fortfahren!
quelle
foo(bar& x) { x.a = 3; }
ist viel zuverlässiger (und lesbarer!) Alsfoo(bar* x) {if (!x) throw std::invalid_argument("x"); x->a = 3;
ref
Schlüsselwort in C #).is shared_ptr intended to never be null? Much as (I think) unique_ptr is?
beiden Annahmen sind falsch.unique_ptr
undshared_ptr
kann null /nullptr
Werte enthalten. Wenn Sie sich keine Gedanken über Nullwerte machen möchten, sollten Sie Referenzen verwenden, da diese niemals Null sein können. Sie müssen auch nicht tippen->
, was Sie als ärgerlich empfinden :)Übergeben Sie Parameter als Wert, wenn Sie innerhalb des Funktionskörpers eine Kopie des Objekts benötigen oder nur das Objekt verschieben müssen. Übergeben
const&
Sie, wenn Sie nur nicht mutierenden Zugriff auf das Objekt benötigen.Beispiel für eine Objektkopie:
Beispiel für Objektverschiebung:
Beispiel für einen nicht mutierenden Zugriff:
Weitere Informationen finden Sie in diesen Blog-Beiträgen von Dave Abrahams und Xiang Fan .
quelle
Die Signatur einer Funktion sollte den Verwendungszweck widerspiegeln. Die Lesbarkeit ist auch für den Optimierer wichtig.
Dies ist die beste Voraussetzung für einen Optimierer, um den schnellsten Code zu erstellen - zumindest theoretisch und wenn nicht in der Realität, dann in einigen Jahren Realität.
Leistungsüberlegungen werden im Zusammenhang mit der Parameterübergabe sehr oft überbewertet. Perfekte Weiterleitung ist ein Beispiel. Funktionen wie
emplace_back
sind meistens sehr kurz und sowieso inline.quelle