Referenz als Rückgabetyp
-
Hallo zusammen,
ich wiederhole gerade Funktionen, die als Rückgabetyp eine Referenz hat. Nun frage ich mich, wann es sich lohnt, eine Referenz zurückzugeben.Wenn ich eine Referenz als Parameter übergebe und diese dann verändere, dann benötige ich selbstverständlich keinen Rückgabewert, da durch das Ändern der Referenz auch die Variable geändert wird.
Wenn ich in der Methode selbst ein Objekt definiere und dieses als Referenz zurückgebe, dann muss dieses static sein, denn sonst ist die Referenz nicht mehr initialisiert. Das sähe dann so aus:
string& foo() { static str = "Hello World"; // static ist Pflicht return str; } int main() { cout << foo; }Ich schätze mal, dass letzterer Fall eher schlecht ist. Könnt ihr mir ein gutes Beispiel geben, wann eine Funktion wirklich eine Referenz als Rückgabetyp haben sollte?
Vielen Dank
lg, freakC++
-
class MyClass { MyClass& operator= (const MyClass&); } MyClass& MyClass::operator= (const MyClass&) { ... return this; } // ermöglicht: a = a = b;MfG SideWinder
-
Danke für das Beispiel. Das ist einleuchtend. Aber dann müssen doch "a" und "b" vom Typ MyClass sein?
Ich habe noch eine andere Frage. Mein Lehrbuch sagt, dass eine Referenz keinen Speicherplatz benötigt, da es sich dabei eben nur um einen Aliasnamen handelt. Doch irgendwo muss doch auch der Name der Referenz und das referenzierte Objekt gespeichert sein. Wie hat man das zu verstehen, dass hier kein Speicherplatz benötigt wird?
Vielen Dank
lg, freakC++
-
freakC++ schrieb:
Ich schätze mal, dass letzterer Fall eher schlecht ist. Könnt ihr mir ein gutes Beispiel geben, wann eine Funktion wirklich eine Referenz als Rückgabetyp haben sollte?
Das trifft man oft bei Memberfunktionen an, aber nicht nur. Beispiele (nicht ganz korrekte C++-Syntax):
template <typename T> T& std::vector<T>::operator[] (size_t index); template <typename T> const T& std::max(const T& lhs, const T& rhs); const std::string& Person::GetName() const;Oft will man mit Referenzen auf
constnur die Kopie sparen. Bei Referenzen auf veränderliche Objekte erlaubt man Modifikationen des referenzierten Objekts, z.B. beimyVector[3] = 5;
-
freakC++ schrieb:
Mein Lehrbuch sagt, dass eine Referenz keinen Speicherplatz benötigt, da es sich dabei eben nur um einen Aliasnamen handelt. Doch irgendwo muss doch auch der Name der Referenz und das referenzierte Objekt gespeichert sein. Wie hat man das zu verstehen, dass hier kein Speicherplatz benötigt wird?
Dein Lehrbuch ist nicht ganz richtig. Es ist vom Standard nicht vorgeschrieben, ob eine Referenz Speicher benötigt oder nicht. In vielen Fällen muss dies so sein, teilweise kann aber auch die Referenz gleich durch das Originalobjekt ersetzt werden.
Für den Programmierer ist diese Frage aber irrelevant, da er ohnehin keine Möglichkeit hat, auf den Speicher der Referenz zuzugreifen.
-
SideWinder schrieb:
class MyClass { MyClass& operator= (const MyClass&); } MyClass& MyClass::operator= (const MyClass&) { ... return this; } // ermöglicht: a = a = b;MfG SideWinder
MyClass& MyClass::operator= (const MyClass&) { ... return *this; }
-
SUUUPER! Ich verstehe ;).
Nun soll eigentlich folgender Code eine Fehlermeldung ausgeben, doch mein Compiler findet keinen. Könntet ihr mir sagen, ob ich mit meinem Verständnis richtig liege?
#include <iostream> using namespace std; int& max(int x, int y) { return x > y ? x : y; } int main() { cout << max(5,6); }Zunächst werden zwei Objekte in die Funktion "max" kopiert (es werden also neue temporäre Objekte angelegt, da die Übergabge per Call by Value stattfindet). Es wird eine Fallunterscheidung durchgeführt. Nun wird das lokale Objekt, welches durch den Kopiervorgang entstanden ist, referenziert und zurückgegeben. Eigenlich müsste das doch auch static sein, denn am Ende der Funktion wird das Objekt wieder gelöscht. Warum funktioniert der Code dann?
Vielen Dank
lg, freakC++
-
Syntaktisch ist er korrekt. Alles, was du von einem Compiler erwarten kannst ist eine Warning.
-
freakC++ schrieb:
Danke für das Beispiel. Das ist einleuchtend. Aber dann müssen doch "a" und "b" vom Typ MyClass sein?
Jain...
Syntaktisch kann b auch ein Objekt sein, das von A erbt,
jedoch geht das inhaltlich (eigentlich immer) schief...
-
Aber die Objekte in der Funktion werden doch beim Ende wieder gelöscht. Warum können diese dann ohne static referenziert werden und außerhalb der FUnktion verwendet werden?
@manni66: An den Fall habe ich nicht gedacht, doch das ist auch nicht der Alltag bei mir xD xD
Danke!!!
lg, freakC++
-
freakC++ schrieb:
Warum funktioniert der Code dann?
Es wurde dir doch auch schon gesagt, dass du bei undefiniertem Verhalten keine Rückschlüsse auf die Korrektheit ziehen darfst. Der Code ist falsch.
Richtig wäre
int& max(int& x, int& y) { return x > y ? x : y; }
-
freakC++ schrieb:
Aber die Objekte in der Funktion werden doch beim Ende wieder gelöscht. Warum können diese dann ohne static referenziert werden und außerhalb der FUnktion verwendet werden?
Richtig... Genau das ist das Problem...
Dein Objekt ist gelöscht, aber deine Referenz verweist noch auf den Speicherbereich
-
Ok, ich hätte nicht gededacht, dass das auch undefiniertes Verhalten ist. Für mich sieht es rein optisch nämlich noch sehr definiert aus. Aber wenn ihr das auch so seht, dann bin ich ja beruhigt! Ich hätte erwartet, dass irgendein Murks ausgegeben wird oder dass eine Fehlermeldung erscheint.
Vielen Dank für die Erklärungen!
lg, freakC++
-
Das ist ja das Gefährliche: Man sieht undefiniertes Verhalten oft erst spät oder gar nicht. Deshalb nie darauf verlassen, dass es kracht, sondern den Code im Zweifelsfalle selbst auf Korrektheit überprüfen.
Zum Beispiel kann ein nicht initialisierter Zeiger auf gültigen Speicher zeigen, wodurch Zugriffe andere Daten der Applikation beeinträchtigen können. Sowas resultiert oft in extrem mühsamen und schwer nachvollziehbaren Bugs.
-
Stimmt! Deswegen sind logische Fehler meiner Ansicht nach auch die schlimmsten. Syntaktische dagegen können einfach behoben werden und man wird sogar bis zur entsprechenden Zeile befördert.
Aber nunja! Wieder was gelernt.
lg, freakC++
-
freakC++ schrieb:
Ich hätte erwartet, dass irgendein Murks ausgegeben wird oder dass eine Fehlermeldung erscheint.
Erstell vor der Ausgabe noch ein paar Objekte auf dem Stack und dein Inhalt ist dann überschrieben.
Dann gibt er dir auch Murks aus
Eine Laufzeit-Fehlermeldung gibt es (sehr wahrscheinlich), wenn dort ein Zeiger
war und du über den Zeiger danach wieder auf das Objekt zugreifen möchtest.