2012-08-10 6 views
15

Mögliche Duplizieren:
Is it better in C++ to pass by value or pass by constant reference?Warum würden Sie ein Objekt nach Wert in C geben ++

Ich bin mir bewusst, die Unterschiede von in C++ von Wert, Zeiger und Referenz vorbei, und Ich würde in Erwägung ziehen, Objekte nach Wert (anstelle von const) in C++ zu übergeben, um fast immer einen Programmierfehler zu sein.

void foo(Obj o); ... // Bad 

void foo(const Obj &o); ... // Better 

Der einzige Fall, ich denken kann, wo es sinnvoll sein könnte von Wert zu übergeben, anstatt konstanter Referenz ist, wo das Objekt kleiner als eine Referenz, und nach Wert vorbei ist daher effizienter.

Aber das ist sicher die Art von Sache, die Compiler gebaut werden, um zu bestimmen?

Warum C++ eigentlich müssen Pässe von Wert und übergibt durch konstante Referenz, und - sind Compiler erlaubt, automatisch den Anruf zu (und von) einer konstanten Referenz falls zu konvertieren?

(Es scheint 100s von C++ Aufrufkonvention Frage, Fragen über die Unterschiede zwischen (sagen wir) Wert und Referenz zu sein - aber ich nicht finden konnte, das „Warum?“ Fragte.)

+2

Was ist mit jedem Mal, wenn Sie eine Kopie benötigen? Füge 'Obj o2 (o);' als erste Zeile hinzu? Das scheint ziemlich sinnlos. Verschieben Sie auch Semantik. – delnan

+0

@ delnan - Danke. Normalerweise tue ich das, was du beschreibst, denn ich denke, das ist ein Implementierungsdetail der Funktion, das nicht Teil der externen Signatur sein sollte. – Roddy

+0

@Nemo. Ja dank. Gewählt, um zu schließen! Leider ist der Bienenstock-Verstand ein besseres Suchwerkzeug als das eingebaute SO ... – Roddy

Antwort

4

Wenn ich wollte, ohne in der Funktion Dinge zu dem Objekt zu tun, Auswirkungen auf das Original, ich Wert übergeben würde:

A minus(A b){ 
    b.val=-b.val; 
    return b; 
} 
1

Wenn das Objekt wandelbar ist, nach Wert vorbei gibt den Empfänger seine eigene Kopie zu verwenden und wo sinnvoll ändern , ohne die Kopie des Anrufers zu beeinflussen - immer vorausgesetzt, es ist eine ausreichend tiefe Kopie.

Dies kann das Denken in einigen Multithread-Situationen vereinfachen.

10

Die Frage der Wertübergabe ist möglicherweise besser als die Konstante const, hat unterschiedliche Antworten auf verschiedene Versionen des Standards.

In der guten alten C++ 03, und vor ein paar Jahren, die Empfehlung wäre, alles, was nicht in ein Register passt durch const Referenz übergeben. In diesem Fall wäre die Antwort:

  • Da Obj paßt in einem Register und vorbei an Wert und vorbei Wert wird effiziente

Noch in C++ 03, in den letzten Jahren (absurd, wie es scheint, einige Artikel empfohlen, diese fast 10 Jahre zurück, aber es gab keinen wirklichen Konsens),

  • , wenn die Funktion muss, um eine Kopie zu machen, dann so in der Schnittstelle zu tun ermöglicht den Compiler pro Formular copy-elision Wenn die Quelle für die Kopie ein temporäres ist, so kann es effizienter sein.

Mit der Genehmigung des neuen Standard 11 C++ und zunehmender Compiler-Unterstützung für rvalue Verweise, auch in vielen Fällen, wenn die Kopie nicht, elided werden kann und wieder

  • , wenn die Funktion muss, um eine Kopie zu machen, auch wenn die Kopie nicht elided werden kann, und für Arten, die es unterstützen, wird der Inhalt sein bewegt (gemeinsam Jargon der Objekt wird verschoben werden, aber es ist nur die Inhalte, die verschoben werden), was wiederum wird ist effizienter als intern kopieren.

Bei der Frage, warum die zwei verschiedenen Aufrufkonventionen, haben sie unterschiedliche Ziele. Wenn Sie den Wert übergeben, kann die Funktion den Status des Arguments ändern, ohne das Quellobjekt zu beeinträchtigen. Außerdem stört der Status des Quellobjekts die Funktion nicht (betrachten Sie eine Multithread-Umgebung und einen Thread, der die Quelle ändert, während die Funktion noch ausgeführt wird).

1

Warum benötigt C++ tatsächlich den Wert und die Konstante const, und - können Compiler den Aufruf automatisch in eine (und von) einer const-Referenz konvertieren, wenn dies angemessen ist?

Lassen Sie mich zuerst die zweite beantworten: manchmal.

Compiler dürfen die Kopie in den Parameter einfügen, aber nur, wenn Sie einen Rvalue temporär übergeben. Zum Beispiel:

void foo(Obj o); 

foo((Obj()))); //Extra set of parenthesis are needed to prevent Most Vexing Parse 

Das Kopieren der temporären in das Argument Parameter kann elided werden (dh: nicht kopiert), auf die Bequemlichkeit des Compiler).

Doch diese Kopie wird nie elided werden:

Obj a; 
foo(a); 

nun auf den ersten. C++ benötigt beide, weil Sie beide für verschiedene Dinge verwenden möchten. Der Übergabewert ist nützlich für die Übertragung von Eigentumsrechten. Das ist wichtiger in C++ 11, wo wir Objekte verschieben und nicht kopieren können.

+0

Danke. "Wertüberschreitung ist nützlich für die Übertragung von Eigentumsrechten" Könnten Sie nur ein wenig ausarbeiten? – Roddy

3

Das Copy-Swap-Idiom verwendet passe by value, um eine Compiler-generierte Kopie zu erzielen.

Grundsätzlich in Situationen, in denen Sie den Parameter ändern müssen, aber nicht das Original berühren möchten. In diesen Situationen müssen Sie, wenn Sie den const-Verweis übergeben, manuell eine Kopie in der Funktion erstellen. Diese manuellen Schritte verhindern bestimmte Optimierungen, die der Compiler ausführen kann, wenn Sie zulassen, dass der Compiler die Kopie verarbeitet.

MyClass a; 
// Some code 
a = MyClass(); // reset the value of a 
       // compiler can easily elide this copy. 
6

Sicherlich ein Grund, C++ hat pass-by-Wert ist, weil sie es von C geerbt, und das Entfernen könnte Code für wenig Gewinn brechen.

Zweitens, wie Sie bemerken, für Typen, die kleiner sind als eine Referenz, die nach Wert geht, wäre weniger effizient.

Ein anderer, weniger offensichtlicher Fall jedoch, wenn Sie eine Funktion, die aus irgendeinem Grunde eine Kopie des Arguments muss:

void foo(const Obj& obj) 
{ 
    if(very_rare_check()) return; 

    Obj obj_copy(obj); 
    obj_copy.do_work(); 
} 

In diesem Fall beachten Sie, dass Sie eine Kopie sind zwingen. Aber angenommen, rufen Sie diese Funktion mit dem Ergebnis einer anderen Funktion, die von Wert zurückgibt:

Obj bar() { return Obj(parameters); } 

Und nennen es so: foo(bar());

Wenn Sie nun die konstante Referenz-Version verwenden, wird der Compiler am Ende zwei machen Objekte: Das temporäre und die Kopie in foo. Wenn Sie jedoch einen Wert übergeben, kann der Compiler alle Provisorien auf die Position umstellen, die von dem by-value-Parameter foo verwendet wird.

Es gibt einen großen Artikel über diese und

schließlich die kanonische Weise bestimmte Betreiber bei http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ Semantik im allgemeinen bewegen zu implementieren ist durch Wertpass verwenden Kopien innerhalb des Bedieners zu vermeiden:

Obj operator+(Obj left, const Obj& right) 
{ 
    return left += right; 
} 

Beachten Sie, wie der Compiler die Kopie im Parameter generieren kann, anstatt eine Kopie oder ein temporäres Objekt innerhalb des Codes des Operators zu erzwingen.

+0

+1 für den "Wollen Sie Geschwindigkeit? Pass by Value" Artikel –