C ++: Rückgabe durch Referenz- und Kopierkonstruktoren
Referenzen in C++ sind verwirren mich. 🙂
Die grundlegende Idee ist, dass ich versuche, ein Objekt wieder aus einer Funktion. Ich würde es gerne spielen, ohne Rückgabe einen Zeiger (weil dann müsste ich manuell delete
es), und ohne den Aufruf des copy-Konstruktors, wenn möglich (für Effizienz, natürlich Hinzugefügt: und auch da Frage ich mich, ob ich nicht vermeiden können, schreiben Sie einen copy-Konstruktor).
So, alles in allem, hier sind die Möglichkeiten, die ich gefunden habe:
- Die Funktion der return-Typ kann entweder in der Klasse selbst (
MyClass fun() { ... }
) oder eine Referenz auf die Klasse (MyClass& fun() { ... }
). - Die Funktion kann entweder konstruieren die variable in der Zeile return (
return MyClass(a,b,c);
) oder senden Sie eine bereits vorhandene variable (MyClass x(a,b,c); return x;
). - Den code, erhält die variable kann auch eine variable entweder: (
MyClass x = fun();
oderMyClass& x = fun();
) - Der code, erhält die variable kann entweder erstellen Sie eine neue variable (
MyClass x = fun();
) oder ordnen Sie eine bereits vorhandene variable (MyClass x; x = fun();
)
Und einige Gedanken auf:
- Es scheint eine schlechte Idee zu haben, ist der Rückgabetyp
MyClass&
weil das immer die Ergebnisse in die variable zerstört wird, bevor es zurückgegeben wird. - Der copy-Konstruktor nur scheint, sich zu engagieren, wenn ich nach einer existierenden variable. Bei der Rückkehr eine variable konstruiert, die in der Linie der Rücksendung, es wird nie genannt.
- Wenn ich weisen Sie das Ergebnis auf eine vorhandene variable, der Destruktor auch immer einspringt, bevor der Wert zurückgegeben wird. Auch kein copy-Konstruktor aufgerufen wird, noch Ziel-variable erhält das Mitglied Werte des Objekts von der Funktion zurückgegeben.
Diese Ergebnisse sind so widersprüchlich, dass ich fühle mich Total verwirrt. Also, was GENAU ist hier passiert? Wie sollte ich richtig konstruieren und kehren Sie ein Objekt aus einer Funktion?
InformationsquelleAutor der Frage Vilx- | 2010-02-16
Du musst angemeldet sein, um einen Kommentar abzugeben.
Der beste Weg, um zu verstehen kopieren in C++ ist oft NICHT zu versuchen, erzeugen künstlich ein Beispiel und instrument - der compiler erlaubt, um beide zu entfernen und fügen Sie der Kopie-Konstruktor-Aufrufe, mehr oder weniger, wie es gerade passt.
Bottom line - wenn Sie einen Rückgabewert, einen Wert zurückzugeben, und Mach dir keine sorgen über irgendwelche "Kosten".
InformationsquelleAutor der Antwort
Empfohlene Lektüre: Effective C++ von Scott Meyers. Sie finden eine sehr gute Erklärung zu diesem Thema (und vieles mehr).
In Kürze, wenn Sie die Rückkehr durch den Wert, den copy-Konstruktor und der Destruktor werden beteiligt sein, standardmäßig (es sei denn der compiler optimiert das Weg - das ist, was passiert in einigen Fällen).
Wenn Sie die Rückgabe per Referenz (oder Zeiger) wird eine variable lokalen gebaut wurde, auf dem stack), laden Sie ärger, weil das Objekt erst dann zerstört wird, bei der Rückgabe, so haben Sie einen baumelnden Referenz als Ergebnis.
Den kanonischen Weise zu konstruieren, die ein Objekt in einer Funktion zurückgeben es ist von Wert, wie:
Wenn Sie diese, brauchen Sie nicht zu befürchten, Eigentumsfragen, hängende Referenzen etc. Und der compiler wird wahrscheinlich optimieren Sie die extra copy-Konstruktor /Destruktor-Aufrufe für Sie, so dass Sie nicht brauchen, um über die Leistung.
Es ist möglich Rückgabe durch Referenz ein Objekt konstruiert, indem
new
(d.h. auf dem heap) - das Objekt wird nicht zerstört werden, nach der Rückkehr aus der Funktion. Jedoch, Sie haben zu zerstören, die es explizit irgendwo später durch aufrufendelete
.Ist es auch technisch möglich, ein Objekt zurückgegebenen Wert in eine Referenz, wie:
Aber AFAIK gibt es nicht sehr viel Sinn, dies zu tun. Vor allem, weil man leicht geben Sie diese Referenz, um andere Teile des Programms, die außerhalb des aktuellen Bereichs; jedoch die referenzierten Objekts durch
x
ist ein lokales Objekt, das zerstört werden wird, sobald Sie aus dem aktuellen Bereich. So in diesem Stil kann führen, um böse bugs.InformationsquelleAutor der Antwort Péter Török
Lesen über RVO und NRVO (in einem Wort, diese zwei steht für Return-Wert-Optimierung und Benannt RVO, und-Optimierung Techniken, die von dem compiler zu tun, was du versuchst zu erreichen)
finden Sie eine Menge von Themen hier auf stackoverflow
InformationsquelleAutor der Antwort f4.
Über die nur Zeit, es macht Sinn für die Rückgabe einer Referenz, wenn Sie der Rückgabe einer Referenz auf ein bereits vorhandenes Objekt. Für ein offensichtliches Beispiel, fast jede iostream-member-Funktion liefert eine Referenz auf die iostream. Die iostream-selbst existiert, vor allen den member-Funktionen aufgerufen wird, und weiterhin zu existieren, nachdem Sie genannt sind.
Ermöglicht der standard mit "copy elision", was bedeutet, dass der copy-Konstruktor nicht aufgerufen werden müssen, wenn Sie ein Objekt zurückgeben. Dieser kommt in zwei Formen: Name Return Value Optimization (NRVO) und anonyme Return-Wert-Optimierung (in der Regel nur RVO).
Von dem, was du sagst, dein compiler implementiert RVO aber nicht NRVO-was bedeutet, es ist wahrscheinlich einen etwas älteren compiler. Aktuelle Compiler implementieren Sie beide. Die un-abgestimmt dtor in diesem Fall bedeutet es wohl so etwas wie gcc 3.4 oder so ungefähr-obwohl ich erinnere mich nicht, die version für sicher, es war eine ein-um dann, die einen Fehler wie diesen. Natürlich ist es auch möglich, dass die instrumentation ist nicht ganz richtig, also einen ctor, dass Sie nicht instrument verwendet wird, und eine dazu passende dtor aufgerufen, für das Objekt.
Am Ende, Sie stecken mit einer einfachen Tatsache, obwohl: wenn Sie brauchen, um ein Objekt zurückgeben, müssen Sie ein Objekt zurückgeben. Insbesondere kann eine Referenz nur den Zugang zu einer (eventuell modifizierten version) ein vorhandenes Objekt, aber das Objekt war zu sein gebaut an einem gewissen Punkt auch. Wenn Sie ändern können einige bestehende Objekt, ohne ein problem verursacht, das ist fein und gut, gehen Sie voran und es tun. Wenn Sie ein neues Objekt anders und getrennt von denen, die Sie bereits haben, gehen Sie vor und tun -- vor dem erstellen des Objekts und übergeben eine Referenz zu machen, die sich selbst zurückgeben schneller, aber nicht speichern jedes mal insgesamt. Erstellen des Objekts hat in etwa die gleiche Kosten-egal, ob innerhalb oder außerhalb der Funktion. Jeder halbwegs moderne compiler RVO, so dass Sie nicht zahlen keine extra Kosten für die Erstellung in der Funktion, dann wird es wieder -- der compiler einfach automatisieren zuweisen von Speicherplatz für das Objekt, an dem Sie zurückgegeben werden, und haben die Funktion konstruieren, die es "in-place", wo Sie noch zugänglich sein, nachdem die Funktion zurückkehrt.
InformationsquelleAutor der Antwort Jerry Coffin
Wenn Sie ein Objekt erstellen, wie diese:
dann wird es auf dem stack der Funktion frame. Wenn die Funktion endet, der Rahmen ist spontan aus dem Stapel und alle Objekte in diesem Rahmen werden zerstört. Es gibt keine Möglichkeit, dies zu vermeiden.
Wenn Sie So wollen, ein Objekt wieder zu einem Anrufer, Sie nur Optionen sind:
Versuch, zu konstruieren, ein lokales Objekt, und dann wieder einen Verweis auf den lokalen Speicher einer aufrufenden Kontext ist nicht kohärent - eine Berufung, die Rahmen kann nicht auf den Speicher zugreifen, die lokal auf der genannt Umfang. , Der lokale Speicher ist nur gültig für die Dauer der Funktion, die Sie besitzt - oder, eine weitere Möglichkeit, während der Ausführung bleibt in diesem Rahmen. Sie müssen verstehen, dass das Programm in C++.
InformationsquelleAutor der Antwort Tom
Im Grunde, die einen Verweis macht nur Sinn, wenn das Objekt existiert noch immer, nach verlassen der Methode. Der compiler warnt Sie, wenn Sie wieder einen Verweis auf etwas, das zerstört wird.
Rückgabe einer Referenz anstatt ein Objekt von Wert, spart Sie das Objekt kopieren, das könnte wichtig sein.
Verweise sind sicherer als Zeiger, da Sie unterschiedliche symantics, aber hinter den kulissen sind Sie Zeiger.
InformationsquelleAutor der Antwort user231967
Eine mögliche Lösung, die vom Anwendungsfall abhängig sind, ist die default-Konstrukt das Objekt außerhalb der Funktion, nehmen in eine Referenz darauf, und initialisieren Sie das referenzierte Objekt innerhalb der Funktion, in etwa so:
Nun ist diese natürlich nicht funktionieren, wenn es nicht möglich (oder nicht sinnvoll) Standard-Konstrukt eine
Foo
- Objekt und initialisiert es später. Wenn das der Fall ist, dann ist Ihre einzige wirkliche option zu vermeiden, copy-Konstruktion ist die Rückgabe einen Zeiger auf eine heap-Objekt zugeordnet.Aber dann darüber nachdenken, warum Sie versuchen, zu vermeiden, kopieren-Bau in den ersten Platz. Die "Kosten" der copy-Konstruktion Auswirkung auf das Programm oder ist dies ein Fall von premature optimization?
InformationsquelleAutor der Antwort Tyler McHenry
Du bist stucked mit entweder:
1) gibt einen Zeiger
MyClass* func(){
//einige stuf
return new MyClass(a,b,c);
}
2) gibt eine Kopie des Objekts
MyClass func(){
return MyClass(a,b,c);
}
Rückgabe einer Referenz ist nicht gültig, weil das Objekt wird zerstört, nach dem verlassen des func-Rahmen, außer wenn die Funktion ist ein Mitglied der Klasse und der Bezug ist aus einer variable, die member der Klasse.
InformationsquelleAutor der Antwort no_ripcord
Nicht eine direkte Antwort, aber eine brauchbare Anregung: Man könnte sich auch wieder ein Zeiger, eingewickelt in ein auto_ptr oder smart_ptr. Dann werden Sie kontrollieren können, was Konstruktoren und Destruktoren aufgerufen werden und wenn.
InformationsquelleAutor der Antwort florin