Referenz als Rückgabewert einer Funktion
-
Hi !!!
Ich bin C++ am lernen und ich verstehe eine Sache mit Referenzen nicht so ganz.
Wenn ich folgende Funktion habe:... int &fkt( int &a ){ return a++; } int c = 3; cout << fkt(c) << endl; ...
Das das Argument der Funktion eine Referenz sein muss ist mir auch klar.
Was ich bisher hoffentlich richtig verstanden habe ist, dass eine Referenz als ein zweiter Name für dieselbe Variable verstanden werden kann, d.h.:
int a=4;
int &b = a;
heisst, dass b sich genauso Verhält wie a und alle Änderungen an b sich auf a auswirken und umgekehrt. In dem Falle der Funktion hat das c quasi in der Funktion noch den Namen a.Doch was ich nicht verstehe ist, warum der Rückgabewert der Funktion eine Referenz ist. Was ist hier der äquivalente Name? Gibt es überhaupt einen? Wenn nein, warum dann kann es sinnvoll sein als Rückgabewert einer Funktion eine Referenz zu haben? Wie kann ich mir das ganze Vorstellen?
Könnte mir das jemand erklären? (in den Büchern und online habe ich bisher keine verständliche Erklärung gefunden)
-
Du könntest (auch wenn es wenig Sinn macht) nun so etwas schreiben:
int c = 3; ++fkt(c); cout << c << endl;
Der Rückgabewert ist wieder eine Referenz auf c in main.
Gruß
-
Es ist erstmal richtig, das es Rückgabe als Referenz (Return by Reference) heißt. Warum macht man sowas? Es gibt zwei Gründe:
1. Jemand will direkt einen Wert ändern, den die Funktion oder Methode eines Objekts zurück gibt. FIreFLow hat ein Beispiel gegeben, wann man sowas machen kann.
2. Wenn man einen Wert als "Wert" (haha!) zurück gibt, also Return by Value, dann wird ja das Objekt kopiert. Bei simplen Typen wie int, char usw. macht das in der Performance überhaupt nichts. ABer stell dir mal vor, du willst einen String mit 10.000 Zeichen oder einen Vector mit 10.000 komplexen Objekten zurück geben. Diese Dinger werden by-Value kopiert. Das kostet Performance. Und meistens will man sowieso das orig. Objekt zurück geben, deshalb macht man es by-Reference.
Wenn es keine Referenzen in C++ geben würde, würde man beide "Probleme" mit Pointern lösen. Aber das verstehen manche noch weniger als Referenzen.
Außerdem müsste man Pointer immer auf Null überprüfen, bei Referenzen ist es praktisch immer ein gültiges Objekt.
-
Außerdem braucht man Referenzen für Operatorüberladung.
-
Kleine Korrektur:
In diesem Fall ist die Referenz eher blöd, da es eine Ref-auf-Temporary ist (ein Standardkonformer Compiler sollte das auch merken). a++ gibt den alten Wert von a zurück (Temporary) und erhöht a dann. Die zurückgegebene Referenz ist eine Referenz auf eine Kopie von a vor dem Inkrement. return ++a an der Stelle würde funktionieren.
-
bucada schrieb:
warum dann kann es sinnvoll sein als Rückgabewert einer Funktion eine Referenz zu haben?
Ah, ok. Der Sinn ist mir nun klar
Doch ich kann mir das ganze immer noch nicht ganz vorstellen. Ich glaube nun verstanden zu haben wie das ist, wenn ich die Funktionsrückgabe einem konkretem Wert zuweise.int b; b = fkt(c);
Verstehe ich das richtig, dass ich dann quasi direkt auf b "arbeite"?
Was ist aber im Falle des Aufrufs ohne das "b", d.h. einfach "fkt(c)" wie oben?
Worauf arbeite ich da?
Mein Problem ist, dass ich nicht weiss, wie das im Speicher umgesetzt wird. Die Referenz quasi dasselbe wie eine bestimmte Variable. Doch wenn ich als Rückgabewert eine Referenz habe und diese keiner Variablen zugeordnet ist, wo wird dann das Ergebnis (der Rückgabewert) im Speicher abgelegt?
Oder gehe ich da falsch ran, um das zu verstehen?
-
Bei Deinem letzten Codebeispiel wird garnirgendwo eine Referenz übernommen. Wenn fkt eine Referenz zurückgibt, landet eine Kopie des Wertes dieser Referenz in b. Du arbeitest mit b.
EDIT: Schlecht formuliert... Du übergibst die Variable c als Referenz an fkt. D.h. Änderungen an c innerhalb von fkt wirken sich auch auf c innerhalb von main aus. Was auch immer zurückgegeben wird wird hier jedoch nach b kopiert (nicht mehr referenziert)
Wenn Du nur fkt(c) aufrufst, wird der Rückgabewert verworfen.
-
Noch eine Sache verstanden
Ich gebe also mit cout noch den Wert aus, und weg ist er aus dem Speicher.
So langsam nähere ich mich dem Ende meines Unverständnisses über Referenzen.Eine Sache verstehe ich noch nicht.
Was auch immer zurückgegeben wird wird hier jedoch nach b kopiert
Hmm, aber würde das dann nicht im Widerspruch zu der Aussage von Artchi stehen?
Artchi schrieb:
Wenn man einen Wert als "Wert" (haha!) zurück gibt, also Return by Value, dann wird ja das Objekt kopiert. ... Diese Dinger werden by-Value kopiert. Das kostet Performance.
Der Rückgabewert in der Funktion ist doch aber by-Reference und nicht by-Value.
Oder ist mein Aufruf im zweitem Beispiel falsch?
Wird da der Inhalt nach b kopiert wegen dem Zuweisungsoperator = ?Was ist dann in dem Falle, wenn die Funktion im gesamten Programm nur mit
... = fkt(...)
aufgerufen wird?
Macht es dann da doch keinen Unterschied, ob ich sie mit Return by Reference, oder mit Return by Value definiere?
-
Richtig. Sinn der Sache bei großen Objekten ist, entweder die Referenz nur einmal zu benutzen (Beispiel: string) oder eine Referenz zu halten. Wenn Du eine normale Zuweisung machst ist egal ob die Funktion by-Value oder by-Reference zurückgibt, das Ergebnis wird kopiert.
const string& name() { return "Hallo, Welt"; } int& fkt(int& a) { a += 2; return a; } int fkt2(int& a) { return a + 2; } cout << name(); // Referenz ist temporär, Objekt verschwindet nach Benutzung (Ausgabe) int& b = fkt(a); // b ist jetzt a (und a/b wurden um 2 erhöht) int b = fkt(a); // b ist jetzt eine Kopie des Rückgabewertes (und a wurde um 2 erhöht) und entspricht vom Wert her a cout << fkt(a); // a wird ausgegeben, danach verschwindet die Referenz, aber das Original-a ist ja noch vorhanden int& b = fkt2(a); // Fehler, da Referenz auf Wert nicht gebildet werden kann int b = fkt2(a); // b ist eine Kopie von (a + 2)
EDIT: Einen Fall vergessen
-
Es ist nicht ganz das selbe ob man den Rückgabewert von "int foo()" oder von "int& foo()" kopiert. Bei ersterem hast du min 2Kopien (einmal Kopie für den Rückgabewert, einmal Kopie bei der Zuweisung) und bei letzterem min 1Kopie (bei der Zuweisung).
Bei ersterem kann der Compiler uU eine Kopie wegoptimieren.
-
Ah, alles klar.
Nun sind alle Unklarheiten zu diesem Thema beseitigt
Ich danke Euch allen sehr