Rückgabe einer Referenz
-
Beispiel1:
// falsche Rückgabe einer Referenz CNumber& CNumber::operator+(const CNumber& a, const CNumber& b) { CNumber result(a+b); return result; // result wird zerstört, sobald das // Programm Sichtbarkeitsbereich von // operator+ verlässt }Beispiel2:
// erneut fehlerhaft, da Zerstörung von pResult ungewiss ist CNumber& CNumber::operator+(const CNumber& a, const CNumber& b) { CNumber *pResult = new CNumber(a+b); return pResult; } CNumber x,y,z; CNumber sum = x+y+z; // y+z hinterlässt SpeicherleckBeipiel3:
// von mir CNumber CNumber::operator+(const CNumber& a, const CNumber& b) { CNumber result(a + b) ; return result; }return ein lokal erzeugtes Object verursacht nicht zu viel kosten(Aufruf) des KopieConstructor, falls das Object sehr gross ist?
-
1. dürfte klar sein, daß das nicht funktionieren kann - das Objekt wird direkt nach dem return gelöscht und die zurückgegeben Referenz zeigt ins Nirvana.
2. ist schonmal syntaktischer Schwachsinn - wenn du eine Referenz abgeben willst, kannst du keinen Pointer zurückgeben. Und außerdem erzeugt es das beschriebene Speicherleck.
3. ist nunmal in deinem Fall unumgänglich. Und viele Compiler dürften in der Lage sein, den zusätzlichen Konstruktor-Aufruf rauszuoptimieren.
Es gibt übrigens noch eine vierte Möglichkeit:
CNumber& operator+(const CNumber& a,const CNumber& b) { static CNumber res; res=a+b; return res; }(hat leider Probleme mit Aliasing, wenn du z.B. "sum=x+y+z;" aufrufst)
PS: Ist es eigentlich Absicht, daß dein Beispiel-Operator sich selber wieder aufruft?
-
PS: Ist es eigentlich Absicht, daß dein Beispiel-Operator sich selber wieder aufruft?
daran habe ich nicht gedacht, ich habe nur ein Beipiel was geändert.
ist schonmal syntaktischer Schwachsinn - wenn du eine Referenz abgeben willst, kannst du keinen Pointer zurückgeben. Und außerdem erzeugt es das beschriebene Speicherleck.
pointer kann natürlich als Referenz zurückgeben, nur wer soll das löschen?
hat leider Probleme mit Aliasing, wenn du z.B. "sum=x+y+z;" aufrufst
das wär meine nächte Frage:
In C++ ist es vollkommen legal, x=x; zu schreiben. Neben diesem sehr offensichtlichen Gebilde gibt es aber auch viele subtilere, die über Indirektionen auf eine Zuweisung an sich selbst hinauslaufen, sie werden als Aliasing-Problem bezeichnet und können unter Umständen gar nicht verhindert werden.
Das Problem besteht darin, dass eine Zuweisung alle bisher von der linken Seite belegten Ressourcen freigeben will und danach die der rechten Seite kopiert. Genau das scheitert bei einer Zuweisung an sich selbst, die zu kopierenden Ressourcen sind gar nicht mehr vorhanden (sie wurden schließlich freigegeben).Das Problem besteht darin, dass eine Zuweisung alle bisher von der linken Seite belegten Ressourcen freigeben will und danach die der rechten Seite kopiert.
das verstehe ich nicht
-
[quote="netrobot"]
ist schonmal syntaktischer Schwachsinn - wenn du eine Referenz abgeben willst, kannst du keinen Pointer zurückgeben. Und außerdem erzeugt es das beschriebene Speicherleck.
pointer kann natürlich als Referenz zurückgeben, nur wer soll das löschen?
Aber CNumber* ist trotzdem ein anderer Typ als CNumber& - d.h. bei (2) müsstest du korrekterweise "return *pResult;" schreiben.
Das Problem besteht darin, dass eine Zuweisung alle bisher von der linken Seite belegten Ressourcen freigeben will und danach die der rechten Seite kopiert.
das verstehe ich nicht
Kurz gesagt - eine Zuweisung funktioniert üblicherweise in der Reihenfolge "eigenen Speicher aufräumen" - "Daten von Quelle kopieren" (andersrum hättest du ein Speicherleck, da du nach dem Kopieren nicht mehr an die alten Daten rankommst). Wenn Ziel und Quelle aber identisch sind, würde der erste Schritt auch die Daten löschen, die du im zweiten Schritt verwenden willst:
class test { public: test() : data(new int[10]) {} test& operator=(const test& other); private: int* data; }; test& test::operator=(const test& other); { //1. Daten aufräumen delete[] data; //2. neue Daten kopieren data = new int[10]; for(int i=0;i<10;++i) data[i]=other.data[i]; }Bis hierher sieht alles noch in Ordnung aus, aber jetzt kommt das Problem:
int main() { test x; x=x;// BUMM }(Schritt 1 hat (ganz vorbildlich) den data-Pointer von x aufgeräumt - nur leider ist other im obigen Code das selbe Objekt und damit habe ich gerade other.data zerstört - und die nachfolgende for()-Schleife kopiert die Daten im neu angelegten Speicherblock in sich selbst)
-
ber CNumber* ist trotzdem ein anderer Typ als CNumber& - d.h. bei (2) müsstest du korrekterweise "return *pResult;" schreiben.
dann bekomme ich eine Referenz auf ein Object, d.h. ich bekomme ein Object, in meinem Fall ist eine Referenz auf einen Zeiger, der wiederum auf ein Object zeigt.
-
netrobot schrieb:
ber CNumber* ist trotzdem ein anderer Typ als CNumber& - d.h. bei (2) müsstest du korrekterweise "return *pResult;" schreiben.
dann bekomme ich eine Referenz auf ein Object, d.h. ich bekomme ein Object, in meinem Fall ist eine Referenz auf einen Zeiger, der wiederum auf ein Object zeigt.
pResult ist ein Zeiger auf ein CNumber-Objekt, *pResult IST ein CNumber-Objekt - und um eine Referenz zu initialisieren, benötigst du keinen Zeiger, sondern das Objekt auf das er zeigt.
-
das ganze geht auch schön mit templates
http://www.flipcode.com/tutorials/tut_fastmath.shtml
rapso->greets();
-
Returnwertoptimierung:
const CNumber CNumber::operator+(const CNumber& a, const CNumber& b) { return result(a+b); }
-
In welchem Kapitel des Standards steht das und vor allem welcher Compiler setzt das auch um

-
CStoll (off) schrieb:
Kurz gesagt - eine Zuweisung funktioniert üblicherweise in der Reihenfolge "eigenen Speicher aufräumen" - "Daten von Quelle kopieren" (andersrum hättest du ein Speicherleck, da du nach dem Kopieren nicht mehr an die alten Daten rankommst). Wenn Ziel und Quelle aber identisch sind, würde der erste Schritt auch die Daten löschen, die du im zweiten Schritt verwenden willst:
class test { public: test() : data(new int[10]) {} test& operator=(const test& other); private: int* data; }; test& test::operator=(const test& other); { //1. Daten aufräumen delete[] data; //2. neue Daten kopieren data = new int[10]; for(int i=0;i<10;++i) data[i]=other.data[i]; }Bis hierher sieht alles noch in Ordnung aus, aber jetzt kommt das Problem:
int main() { test x; x=x;// BUMM }(Schritt 1 hat (ganz vorbildlich) den data-Pointer von x aufgeräumt - nur leider ist other im obigen Code das selbe Objekt und damit habe ich gerade other.data zerstört - und die nachfolgende for()-Schleife kopiert die Daten im neu angelegten Speicherblock in sich selbst)
Wobei man dazusagen sollte, dass man einen op= so niemals aufbauen sollte. Entweder man setzt den alten swap Trick ein oder achtet auf die korrekte Reihenfolge. Und das heisst, erst neuen Speicher reservieren und dann den alten freigeben. Dann funktioniert nicht nur Selbstzuweisung, der Code ist zudem exception-safe.
test& test::operator=(const test& other) { //1. neue Daten kopieren tmp = new int[10]; for(int i=0;i<10;++i) tmp[i]=other.data[i]; //2. Daten aufräumen delete[] data; data = tmp; }