Frage

Ist es besser in C++ zu pass-by-value oder pass-durch ständige Referenz?

Ich Frage mich, was ist besser Praxis.Ich merke, dass pass durch ständige Verweis sollte für eine bessere Leistung des Programms, da Sie keine Kopie der Variablen.

War es hilfreich?

Lösung

Es wird verwendet, im Allgemeinen am besten Praxis zu empfehlen 1 Verwendung Pass von const ref für alle Typen , mit Ausnahme von Typen (char, int, double gebautet, etc.), für Iteratoren und Funktionsobjekte (Lambda-Ausdrücke, Ableiten Klassen von std::*_function).

Dies gilt vor allem vor der Existenz von Bewegung Semantik . Der Grund ist einfach:., Wenn Sie von Wert übergeben, wird eine Kopie des Objekts gemacht werden muß, und, mit Ausnahme von sehr kleinen Objekten, das ist immer teurer als eine Referenz geben

Mit 11 C ++, haben wir gewonnen bewegen Semantik . Auf den Punkt gebracht, bewegen Semantik erlauben, dass in einigen Fällen kann ein Objekt „durch den Wert“ übergeben werden, ohne sie zu kopieren. Insbesondere ist dies der Fall, wenn das Objekt, das Sie vorbei ein rvalue .

An sich ist es, ein sich bewegendes Objekt durch Bezugnahme noch mindestens so teuer wie vorbei. in vielen Fällen wird jedoch eine Funktion kopieren intern ein Objekt sowieso - das heißt, sie nehmen Eigentum des Arguments 2

.

In diesen Situationen wir folgende (vereinfachte) trade-off haben:

  1. Wir können das Objekt als Verweis übergeben, dann intern kopieren.
  2. Wir können das Objekt als Wert übergeben.

„Pass von Wert“ bewirkt noch das Objekt kopiert werden, es sei denn, das Objekt ein R-Wert ist. Im Fall eines R-Wertes, kann das Objekt stattdessen bewegt werden, so dass der zweite Fall plötzlich nicht mehr „zu kopieren, dann bewegen“, sondern „bewegt, dann (möglicherweise) bewegt wieder“.

Für große Objekte, die richtige Bewegung Konstrukteurs implementieren (wie Vektoren, strings ...), ist der zweite Fall dann in beträchtlichem Ausmaß effizienter als die ersten. Daher ist es von Wert Verwendung Pass zu empfehlen, wenn die Funktion das Eigentum an dem Argument nimmt, und wenn der Objekttyp unterstützt die effiziente Bewegungs .


Eine historische Anmerkung:

In der Tat sollte jeder moderner Compiler in der Lage sein, um herauszufinden, wenn sie von Wert vorbei teuer ist, und konvertieren implizit den Anruf ein const ref zu verwenden, wenn möglich.

In der Theorie. In der Praxis Compiler kann dies nicht immer ohne verändert die Funktion des Binärschnittstelle zu brechen. In einigen Sonderfällen (wenn die Funktion inlined wird) wird die Kopie tatsächlich elided werden, wenn der Compiler herausfinden, dass das ursprüngliche Objekt wird nicht durch die Aktionen in der Funktion geändert werden.

Aber im Allgemeinen der Compiler dies nicht bestimmen können, und das Aufkommen der Bewegung Semantik in C ++ hat diese Optimierung viel weniger relevant gemacht.


1 Z. B. in Scott Meyers, Effective C ++ .

2 Dies ist besonders häufig gilt für Objektkonstruktoren, welche Argumente annehmen und speichern sie intern Teil des konstruierten Objekts Zustand zu sein.

Andere Tipps

Edit: Neue Artikel von Dave Abrahams auf cpp-weiter:

Willst Geschwindigkeit?Pass-by-value.


Pass-by-value für Strukturen, wo das kopieren ist Billig, hat den zusätzlichen Vorteil, dass der compiler kann davon ausgehen, dass die Objekte nicht alias (es sind nicht die gleichen Objekte).Mithilfe von pass-by-reference kann nicht der compiler davon ausgehen, dass immer.Einfaches Beispiel:

foo * f;

void bar(foo g) {
    g.i = 10;
    f->i = 2;
    g.i += 5;
}

der compiler optimieren kann es in

g.i = 15;
f->i = 2;

da er weiß, dass f und g nicht teilen, die gleiche Lage.wenn g ein Verweis (foo &) der compiler konnte nicht davon ausgegangen werden, dass.da g.ich könnte dann mit alias by f->ich und haben einen Wert von 7.so würde der compiler wieder-Holen Sie den neuen Wert von g ist.ich aus dem Gedächtnis.

Weitere praktische Regeln, hier ist ein guter Satz von Regeln finden sich in Move-Konstruktoren Artikel (hoch empfohlen zu Lesen).

  • Wenn die Funktion will das argument als Nebeneffekt, nehmen Sie es von nicht-const-Referenz.
  • Wenn die Funktion nicht, ändern Sie das argument und das argument ist eine primitive Art, nehmen Sie es Wert.
  • Sonst nehmen Sie es durch const Referenz, außer in den folgenden Fällen
    • Wenn das funktionieren würde, dann brauchen Sie, um eine Kopie der const-Referenz trotzdem, nehmen Sie es Wert.

"Primitive" oben bedeutet im Grunde, kleinen Datentypen, die ein paar bytes lang ist und nicht polymorph (Iteratoren, function objects, etc...) oder teuer zu kopieren.In diesem Papier, es ist eine andere Regel.Die Idee ist, dass manchmal will man eine Kopie (falls das argument nicht geändert werden können), und manchmal hat man nicht will (im Falle eines verwenden möchte, das argument selbst in der Funktion, wenn das argument war vorübergehend jedenfalls, zum Beispiel).Das Papier wird ausführlich erklärt, wie dies erreicht werden kann.In C++1x, dass die Technik verwendet werden, die nativ mit Sprache unterstützen.Bis dann, ich würde mit dem oben genannten Regeln.

Beispiele:Um einen string in Großbuchstaben und wieder die Großbuchstaben version, sollte man immer Wert übergeben:Man muss auch eine Kopie von es trotzdem (man konnte es nicht ändern, die const-Referenz direkt) - also besser so transparent wie möglich zu die Anrufer und machen Sie die Kopie so früh, dass der Anrufer zu optimieren, so viel wie möglich, so detailliert in diesem Papier:

my::string uppercase(my::string s) { /* change s and return it */ }

Allerdings, wenn Sie nicht brauchen, um den parameter zu ändern eh, nehmen Sie es durch einen Verweis auf const:

bool all_uppercase(my::string const& s) { 
    /* check to see whether any character is uppercase */
}

Wenn Sie jedoch der Zweck des Parameters ist, etwas zu schreiben, in die Auseinandersetzung ein, dann gehen Sie durch nicht-const-Referenz

bool try_parse(T text, my::string &out) {
    /* try to parse, write result into out */
}

Abhängig von der Art. Sie fügen den kleinen Aufwand für eine Referenz und dereferenzieren machen zu müssen. Bei den Typen mit einer Größe gleich oder kleiner als Zeiger, der die Standardkopie Ctor verwenden, wäre es wahrscheinlich schneller sein als Wert zu übergeben.

Wie darauf hingewiesen wurde, hängt es von der Art. Für integrierte Datentypen, ist es am besten Wert zu übergeben. Sogar einige sehr kleine Strukturen, wie zum Beispiel ein Paar von Ints kann besser durchführen, indem durch Wert übergeben.

Hier ist ein Beispiel, vorausgesetzt, dass Sie einen Integer-Wert haben, und Sie es an einem anderen Routine übergeben werden sollen. Wenn dieser Wert optimiert wurde in einem Register gespeichert wird, dann, wenn Sie es Verweis sein übergeben wollen, muss es zunächst im Speicher gespeichert wird und dann ein Zeiger auf diesen Speicher auf dem Stapel gelegt, um den Anruf durchzuführen. Wenn es von Wert übergeben wurde, alles, was erforderlich ist, ist das Register auf den Stapel geschoben. (Die Details sind etwas komplizierter als die bei unterschiedlichen Rufsysteme und CPUs).

Wenn Sie Template-Programmierung tun, werden Sie in der Regel gezwungen, immer durch const ref zu passieren, da Sie nicht wissen, welche Arten in weitergegeben werden. Passing Strafen für etwas Schlechtes von Wert vorbei sind viel schlechter als die Strafen der Weitergabe einer eingebauten -in Typ von const ref.

Klingt wie Sie Ihre Antwort. Vorbei Wert ist teuer, aber gibt Ihnen eine Kopie mit zu arbeiten, wenn Sie es brauchen.

Das ist, was ich von normalerweise arbeiten, wenn die Schnittstelle einer Nicht-Template-Funktion Entwurf:

  1. nach Wert übergeben, wenn die Funktion der Parameter ändern nicht will, und die Wert ist billig zu kopieren (int, double, float, char, bool, etc ... Beachten Sie, dass std :: string, std :: vector, und der Rest der Behälter in der Standardbibliothek sind NICHT)

  2. Pass von const Zeiger, wenn der Wert teuer zu kopieren und die Funktion macht nicht will, dass der Wert deutete auf und NULL ist ein Wert, zu modifizieren, dass die Funktion behandelt.

  3. Sie passieren nicht konstanten Zeiger, wenn der Wert zu kopieren und die Funktion teuer will der Wert ändern und wies auf NULL ein Wert ist, dass die Funktion behandelt.

  4. Führen durch konstante Referenz, wenn der Wert teuer zu kopieren und die Funktion will nicht den Wert und NULL kein gültiger Wert sein würde, bezeichnet ändern, wenn ein Zeiger verwendet wurde, statt.

  5. Sie passieren nicht konstante Referenz, wenn der Wert teuer zu kopieren und die Funktion will den Wert bezeichnet und NULL würde ändern nicht verwendet wird, wurde stattdessen ein gültiger Wert, wenn ein Zeiger.

In der Regel vorbei konstante Referenz ist besser. Aber wenn Sie ändern müssen funktionieren Sie Argument lokal sollten Sie besser vorbei Wert verwenden. Für einige Grundtypen der Leistung im Allgemeinen für beide gleich nach Wert und durch Verweis übergeben. Eigentlich intern durch Zeiger dargestellt wird Bezug, deshalb kann man zum Beispiel davon aus, dass für beide Zeiger Gang das gleiche in Bezug auf die Leistung sind, oder sogar durch Wert vorbei, weil unnötige dereferenzieren schneller kann.

Als Faustregel gilt: Wert für Nicht-Klasse-Typen und konstante Referenz für die Klassen. Wenn eine Klasse wirklich klein ist, ist es wahrscheinlich besser Wert zu übergeben, aber der Unterschied ist minimal. Was Sie wirklich vermeiden wollen, ist eine gigantische Klasse von Wert vorbei und es dupliziert haben -. Dies wird einen großen Unterschied machen, wenn Sie vorbei, sagen wir, ein std :: vector mit ganz wenigen Elementen in es

Pass von Wert für kleine Typen.

Pass von const Referenzen für große Typen (die Definition von großen Maschinen zwischen variieren kann), aber in C ++ 11, nach Wert übergeben, wenn Sie die Daten verbrauchen werden, da Sie bewegen Semantik ausnutzen können. Zum Beispiel:

class Person {
 public:
  Person(std::string name) : name_(std::move(name)) {}
 private:
  std::string name_;
};

Nun würde der Angerufene Code tun:

Person p(std::string("Albert"));

Und nur ein Objekt geschaffen würden und direkt in Mitglied name_ in Klasse Person bewegt. Wenn Sie mit dem const Verweis übergeben, wird eine Kopie sein machte es in name_ setzen.

Einfacher Unterschied: - In Funktion, die wir haben Eingabe- und Ausgabeparameter, so dass, wenn Ihr Weitergabe Eingang und out-Parameter gleich sind dann durch Referenz verwenden Anruf sonst, wenn Eingangs- und Ausgangsparameter unterschiedlich sind dann besser Aufruf von Wert verwenden

Beispiel void amount(int account , int deposit , int total )

Eingabeparameter: Konto, Depot Ausgang paramteter: total

Eingang und out ist anders Verwendung Aufruf von vaule

  1. void amount(int total , int deposit )

Eingang Gesamt Anzahlung Ausgabe Gesamt

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top