Beim Umgestalten von Code bin ich auf einige Getter-Methoden gestoßen, die einen std :: string zurückgeben. So etwas zum Beispiel:
class foo
{
private:
std::string name_;
public:
std::string name()
{
return name_;
}
};
Sicherlich würde der Getter besser ein const std::string&
? Die aktuelle Methode gibt eine Kopie zurück, die nicht so effizient ist. Würde die Rückgabe einer const-Referenz stattdessen Probleme verursachen?
Antworten:
Dies kann nur dann zu einem Problem führen, wenn der Aufrufer die Referenz speichert, anstatt die Zeichenfolge zu kopieren, und versucht, sie zu verwenden, nachdem das Objekt zerstört wurde. So was:
foo *pFoo = new foo; const std::string &myName = pFoo->getName(); delete pFoo; cout << myName; // error! dangling reference
Da Ihre vorhandene Funktion jedoch eine Kopie zurückgibt, würden Sie keinen der vorhandenen Codes beschädigen.
Bearbeiten: Modernes C ++ (dh C ++ 11 und höher) unterstützt die Rückgabewertoptimierung , sodass das Zurückgeben von Dingen nach Wert nicht länger verpönt ist. Man sollte immer noch darauf achten, extrem große Objekte nach Wert zurückzugeben, aber in den meisten Fällen sollte es in Ordnung sein.
quelle
Tatsächlich ist ein weiteres Problem speziell bei der Rückgabe einer Zeichenfolge, die nicht als Referenz dient, die Tatsache, dass über die c_str () -Methode
std::string
über einen Zeiger auf eine interne Zeichenfolge zugegriffen werden kann. Dies hat mir viele Stunden lang Probleme beim Debuggen verursacht. Nehmen wir zum Beispiel an, ich möchte den Namen von foo erhalten und an JNI übergeben, um einen Jstring zu erstellen, der später an Java übergeben wird. Dabei wird eine Kopie und keine Referenz zurückgegeben. Ich könnte so etwas schreiben:const char*
name()
foo myFoo = getFoo(); // Get the foo from somewhere. const char* fooCName = foo.name().c_str(); // Woops! foo.name() creates a temporary that's destructed as soon as this line executes! jniEnv->NewStringUTF(fooCName); // No good, fooCName was released when the temporary was deleted.
Wenn Ihr Aufrufer so etwas tun wird, ist es möglicherweise besser, einen intelligenten Zeiger oder eine konstante Referenz zu verwenden oder zumindest einen bösen Warnkommentar-Header über Ihrer foo.name () -Methode zu haben. Ich erwähne JNI, weil ehemalige Java-Codierer möglicherweise besonders anfällig für diese Art der Methodenverkettung sind, die ansonsten harmlos erscheint.
quelle
Ein Problem für die Rückgabe der const-Referenz wäre, wenn der Benutzer Folgendes codiert:
const std::string & str = myObject.getSomeString() ;
Bei einer
std::string
Rückgabe würde das temporäre Objekt am Leben bleiben und an str angehängt, bis str den Gültigkeitsbereich verlässt.Aber was passiert mit einem
const std::string &
? Ich vermute, dass wir einen konstanten Verweis auf ein Objekt haben würden, das sterben könnte, wenn sein übergeordnetes Objekt die Zuordnung aufhebt:MyObject * myObject = new MyObject("My String") ; const std::string & str = myObject->getSomeString() ; delete myObject ; // Use str... which references a destroyed object.
Meine Präferenz gilt also der Konstantenreferenzrückgabe (da ich mit dem Senden einer Referenz ohnehin nur bequemer bin als zu hoffen, dass der Compiler die zusätzliche temporäre Option optimiert), solange der folgende Vertrag eingehalten wird: "Wenn Sie ihn darüber hinaus wünschen Die Existenz meines Objekts kopieren sie vor der Zerstörung meines Objekts. "
quelle
Einige Implementierungen von std :: string teilen sich den Speicher mit Copy-on-Write-Semantik, sodass Return-by-Value fast so effizient sein kann wie Return-by-Reference, und Sie müssen sich keine Gedanken über die Lebensdauer machen (die Laufzeit tut dies) es für dich).
Wenn Sie sich Sorgen um die Leistung machen, dann vergleichen Sie diese (<= kann das nicht genug betonen) !!! Probieren Sie beide Ansätze aus und messen Sie den Gewinn (oder das Fehlen davon). Wenn einer besser ist und es dich wirklich interessiert, dann benutze ihn. Wenn nicht, dann bevorzugen Sie einen Nebenwert für den Schutz, den es gegen lebenslange Probleme bietet, die von anderen Personen erwähnt wurden.
Sie wissen, was sie über Annahmen sagen ...
quelle
Okay, die Unterschiede zwischen der Rücksendung einer Kopie und der Rücksendung der Referenz sind:
Leistung : Die Rückgabe der Referenz kann schneller sein oder nicht. Dies hängt davon ab, wie
std::string
Ihre Compiler-Implementierung implementiert wird (wie andere bereits betont haben). Aber selbst wenn Sie die Referenz zurückgeben, beinhaltet die Zuweisung nach dem Funktionsaufruf normalerweise eine Kopie, wie instd::string name = obj.name();
Sicherheit : Die Rücksendung der Referenz kann Probleme verursachen oder nicht (baumelnde Referenz). Wenn die Benutzer Ihrer Funktion nicht wissen, was sie tun, die Referenz als Referenz speichern und verwenden, nachdem das bereitgestellte Objekt den Gültigkeitsbereich verlassen hat, liegt ein Problem vor.
Wenn Sie es schnell und sicher wollen, verwenden Sie boost :: shared_ptr . Ihr Objekt kann die Zeichenfolge intern als speichern
shared_ptr
und a zurückgebenshared_ptr
. Auf diese Weise wird das Objekt nicht kopiert, und es ist immer sicher (es sei denn, Ihre Benutzer ziehen den Rohzeiger mit herausget()
und tun dies, nachdem Ihr Objekt den Gültigkeitsbereich verlassen hat).quelle
Ich würde es ändern, um const std :: string & zurückzugeben. Der Anrufer wird wahrscheinlich trotzdem eine Kopie des Ergebnisses erstellen, wenn Sie nicht den gesamten Anrufcode ändern, dies führt jedoch zu keinen Problemen.
Eine mögliche Falte tritt auf, wenn Sie mehrere Threads haben, die name () aufrufen. Wenn Sie eine Referenz zurückgeben, aber später den zugrunde liegenden Wert ändern, ändert sich der Wert des Anrufers. Der vorhandene Code sieht jedoch ohnehin nicht threadsicher aus.
Schauen Sie sich Dimas Antwort auf ein damit verbundenes potenzielles, aber unwahrscheinliches Problem an.
quelle
Es ist denkbar, dass Sie etwas kaputt machen könnten, wenn der Anrufer wirklich eine Kopie wollte, weil er das Original ändern wollte und eine Kopie davon aufbewahren wollte. Es ist jedoch weitaus wahrscheinlicher, dass tatsächlich nur eine konstante Referenz zurückgegeben wird.
Am einfachsten ist es, es zu versuchen und dann zu testen, um festzustellen, ob es noch funktioniert, vorausgesetzt, Sie haben eine Art Test, den Sie ausführen können. Wenn nicht, würde ich mich darauf konzentrieren, zuerst den Test zu schreiben, bevor ich mit dem Refactoring fortfahre.
quelle
Die Chancen stehen gut, dass die typische Verwendung dieser Funktion nicht unterbrochen wird, wenn Sie zu einer konstanten Referenz wechseln.
Wenn der gesamte Code, der diese Funktion aufruft, unter Ihrer Kontrolle steht, nehmen Sie einfach die Änderung vor und prüfen Sie, ob sich der Compiler beschwert.
quelle
Ist das wichtig? Sobald Sie einen modernen Optimierungs-Compiler verwenden, wird für Funktionen, die nach Wert zurückgegeben werden, keine Kopie verwendet, es sei denn, dies ist semantisch erforderlich.
Lesen Sie hierzu die häufig gestellten Fragen zu C ++ lite .
quelle
Kommt darauf an, was du tun musst. Vielleicht möchten Sie, dass alle Aufrufer den zurückgegebenen Wert ändern, ohne die Klasse zu ändern. Wenn Sie die const-Referenz zurückgeben, wird diese nicht fliegen.
Das nächste Argument ist natürlich, dass der Anrufer dann seine eigene Kopie erstellen könnte. Wenn Sie jedoch wissen, wie die Funktion verwendet wird, und wissen, dass dies trotzdem geschieht, sparen Sie sich möglicherweise einen Schritt später im Code.
quelle
Normalerweise gebe ich const & zurück, es sei denn, ich kann nicht. QBziZ gibt ein Beispiel dafür, wo dies der Fall ist. Natürlich behauptet QBziZ auch, dass std :: string eine Copant-on-Write-Semantik hat, was heutzutage selten der Fall ist, da COW in einer Multithread-Umgebung viel Overhead mit sich bringt. Indem Sie const & zurückgeben, müssen Sie den Anrufer dazu verpflichten, das Richtige mit der Zeichenfolge am Ende zu tun. Da es sich jedoch um Code handelt, der bereits verwendet wird, sollten Sie ihn wahrscheinlich nicht ändern, es sei denn, die Profilerstellung zeigt, dass das Kopieren dieser Zeichenfolge massive Leistungsprobleme verursacht. Wenn Sie sich dann entscheiden, es zu ändern, müssen Sie es gründlich testen, um sicherzustellen, dass Sie nichts kaputt gemacht haben. Hoffentlich machen die anderen Entwickler, mit denen Sie arbeiten, keine skizzenhaften Dinge wie in Dimas Antwort.
quelle
Wenn Sie einen Verweis auf ein Mitglied zurückgeben, wird die Implementierung der Klasse verfügbar gemacht. Das könnte verhindern, dass die Klasse geändert wird. Kann für private oder geschützte Methoden nützlich sein, wenn die Optimierung erforderlich ist. Was sollte ein C ++ - Getter zurückgeben?
quelle