[gelöst] Überladen des ++ Operators (Prä-/Postfix)



  • Hallo!

    Meine Frage bezieht sich auf ein Bauspielprogramm aus dem Buch "C++ von A-Z" von Jürgen Wolf. Ich weiss das sich jetzt einigen hier die Zehennägel rumkrempeln werden und die Hutschnur hochgeht, aber zu meiner Verteidigung muss ich sagen, dass ich das Buch unwissenderweise vor 1 Jahr gekauft habe ohne hier ins Forum zu schauen. Mein Plan war eigentlich in den aktuellen Semesterferien einen guten C++ Einblick zu erhalten und da mir der Stroustrup doch etwas zu schnell startete fiel die Wahl auf den Schinken aus dem Galileo-Verlag der seit dem Kauf unberührt im Regal stand. Wie dem auch sei, hier ist erst einmal das Listing:

    // complex5.cpp
    #include <iostream>
    using namespace std;
    
    class myComplex {
       private:
       double _real;
       double _image;
    
       public:
       myComplex( double val1=0.0, double val2=0.0 ) {
          _real = val1; _image = val2;
       }
       void print_Complex( ) {
          cout << _real << "+" << _image << "\n";
       }
       // Präfix-Operator ++ überladen
       myComplex& operator++() {
          _real++;
          _image++;
          return *this;
       }
       // Postfix-Operator ++ überladen
       myComplex& operator++(int) {
          static myComplex tmp(*this);
          _real++;
          _image++;
          return tmp;
       }
    };
    
     int main( void ) {
       myComplex val1(1.1, 2.2);
       ++val1;
       cout << "Wert von val1: "; val1.print_Complex( );
       val1++;
       cout << "Wert von val1: "; val1++.print_Complex( );
       cout << "Wert von val1: "; val1.print_Complex( );   
       return 0;
    }
    

    Das Problem ist nun folgendes, eigentlich sind es sogar 2 Probleme, erstens lautet die Ausgabe des Programms laut Buch so:

    Wert von val1: 2.1+3.2
    Wert von val1: 3.1+4.2
    Wert von val1: 4.1+5.2

    Nachdem kompilieren des Quelltextes erscheint aber folgende Ausgabe:

    Wert von val1: 2.1+3.2
    Wert von val1: 2.1+3.2
    Wert von val1: 4.1+5.2

    Es ist zwar nicht unbedingt hilfreich, es sei aber an dieser Stelle erwähnt das ich unter Xubuntu 11.04 arbeite und mit gcc/c++ kompiliere.
    Das zweite Problem wäre warum der Autor den Adressoperator an dieser Stelle myComplex*&** operator++()* verwendet, komischerweise wurde das bei den anderen Funktionen zur Operator-Überladung in dem Kapitel nicht so gemacht und lässt sich ohne den Adressoperator auch problemlos kompilieren. Der Rückgabewert der Funktion ist doch auch keine Adresse sondern ein schnödes Objekt, oder?
    Ziemlich viel für den ersten Beitrag aber ich hoffe ihr könnte mir weiterhelfen.


  • Mod

    Das ist kein Adressoperator, sondern eine Referenz und das was Herr Wolf da in der Funktion tut ist, sofern es kein Lehrbeispiel zu static sein soll, eine Sauerei, auch wenn es technisch gesehen ein richtiges Programm ist. Es verhindert aktiv Optimierungen und Parallelisierbarkeit und bietet keine Vorteile.

    Dass das Ergebnis im Buch nicht stimmt, rundet das Gesamtbild ab.

    Wie das mit dem Überladen von Operatoren gut und richtig geht, erfährst du hier:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-232010.html



  • Also es ist schonmal kein Bsp. was die Verwendung von static erläutern soll 😉
    Referenzen verwirren mich immer noch leicht, unter C erhalte ich ja die Adresse wenn ich "&x" verwende, deshalb verdreh ich das gerne mal. Da das nicht der erste Fehler ist den ich in dem Buch entdeckt habe, bei den anderen konnt ich mir selber noch weiterhelfen, wird das wohl zu eBay wandern. Bleibt nur noch die Frage was ich mir als alternativ Literatur besorge.

    PS: Danke für den Link.



  • Zum Ersten: Wer kommt denn auf die clevere Idee, eine static-Variable als temporären Wert für den Post-Inkrement zu verwenden? tmp wird beim allerersten Aufruf des Operators gefüllt und bleibt danach konstant.
    Korrekterweise solltest du das static weglassen (und die Rückgabe per Referenz auch).

    Zweitens: Das & an der Stelle ist kein Adress-Operator, sondern sagt aus, daß die Methode eine Referenz zurückgibt - dadurch ermöglichst du es, das zurückgegebene Objekt direkt weiterzuverarbeiten.

    PS: Zu J.W. sage ich jetzt nichts - aber du hast da gerade einen Grund dafür ausgegraben, warum er hier so unbeliebt ist 😃


  • Mod



  • Ist das Listing jetzt aus'm Buch abgetippt? Falls ja: 😮 So eine gequirlte Kacke in einem Buch zu sehen tut echt weh. Schmeiß es bloß weg und vergiss alles, was Du darin gelesen hast.



  • klingt.net schrieb:

    // complex5.cpp
    #include <iostream>
    using namespace std;
    
    class myComplex {
       private:
       double _real;
       double _image;
       
       public:
       myComplex( double val1=0.0, double val2=0.0 ) {
          _real = val1; _image = val2;
       }
       void print_Complex( ) {
          cout << _real << "+" << _image << "\n";
       }
       // Präfix-Operator ++ überladen
       myComplex& operator++() {
          _real++;
          _image++;
          return *this;
       }
       // Postfix-Operator ++ überladen
       myComplex& operator++(int) {
          static myComplex tmp(*this);
          _real++;
          _image++;
          return tmp;
       }
    };
    
     int main( void ) {
       myComplex val1(1.1, 2.2);
       ++val1;
       cout << "Wert von val1: "; val1.print_Complex( );
       val1++;
       cout << "Wert von val1: "; val1++.print_Complex( );
       cout << "Wert von val1: "; val1.print_Complex( );   
       return 0;
    }
    

    lol 👍
    Das ist soo schlecht, das kann nicht nur aus Unwissenheit entstammen. Jürgen Wolf muß ein Troll sein.



  • CStoll schrieb:

    Zum Ersten: Wer kommt denn auf die clevere Idee, eine static-Variable als temporären Wert für den Post-Inkrement zu verwenden?

    Wahrscheinlich hat ihm jemand gesagt, dass er keine Referenzen auf temporäre Objekte zurückgeben darf. Da hat er die Lebensdauer etwas verlängert 😃

    Mal abgesehen von den ganzen codetechnischen Mängeln: Was soll Inkrementieren bei ner komplexen Zahl überhaupt sein? Ich hätte höchstens erwartet, dass der Realteil inkrementiert wird. So siehts eher aus nach Exkrementieren.



  • klingt.net schrieb:

    Da das nicht der erste Fehler ist den ich in dem Buch entdeckt habe, bei den anderen konnt ich mir selber noch weiterhelfen, wird das wohl zu eBay wandern.

    Bitte nicht. Am Ende kauft das noch jemand.



  • Michael E. schrieb:

    CStoll schrieb:

    Zum Ersten: Wer kommt denn auf die clevere Idee, eine static-Variable als temporären Wert für den Post-Inkrement zu verwenden?

    Wahrscheinlich hat ihm jemand gesagt, dass er keine Referenzen auf temporäre Objekte zurückgeben darf. Da hat er die Lebensdauer etwas verlängert 😃

    Dann sollte man ihm als nächstes beibringen, daß es bei manchen Funktionen nicht sinnvoll ist, Referenzen zurückzugeben 😃

    @volkard: Ist es dir auch schon aufgefallen? JW ist schon lange genug hier verrufen 🕶


  • Mod

    CStoll schrieb:

    @volkard: Ist es dir auch schon aufgefallen? JW ist schon lange genug hier verrufen 🕶

    Bisher galt er aber nur als unfähig, nun sind wir schon auf der Stufe wo man annehmen muss, dass er das mit Absicht macht. 😋



  • CStoll schrieb:

    Dann sollte man ihm als nächstes beibringen, daß es bei manchen Funktionen nicht sinnvoll ist, Referenzen zurückzugeben 😃

    Wartet, bis C++ einen Garbage-Collector hat. *grusel*



  • Die Art es auszugeben gefällt mir 😮 .



  • Dann merke ich noch an, daß der op++(int) den Nutzcode des op++() dupliziert, statt ihn aufzurufen.


  • Mod

    volkard schrieb:

    Dann merke ich noch an, daß der op++(int) den Nutzcode des op++() dupliziert, statt ihn aufzurufen.

    Ich glaube alle Schwächen im Code auf zu zählen, wird ein lange Liste 😃 . Ich mache mal weiter: Initialisierungsliste.



  • EOutOfResources schrieb:

    Die Art es auszugeben gefällt mir 😮 .

    War klar, dass du daran rummeckerst. Es weiß doch jedes Kind, dass man operator<<(ostream&, MyType) auf jeden Fall schon erklären muss, bevor die erste eigene Ausgabe gemacht werden soll, insbesondere vor operator+ .



  • SeppJ schrieb:

    volkard schrieb:

    Dann merke ich noch an, daß der op++(int) den Nutzcode des op++() dupliziert, statt ihn aufzurufen.

    Ich glaube alle Schwächen im Code auf zu zählen, wird ein lange Liste 😃 . Ich mache mal weiter: Initialisierungsliste.

    int main(void)



  • Michael E. schrieb:

    [...]

    Er soll's lassen wenn er's nicht richtig macht...



  • Michael E. schrieb:

    EOutOfResources schrieb:

    Die Art es auszugeben gefällt mir 😮 .

    War klar, dass du daran rummeckerst. Es weiß doch jedes Kind, dass man operator<<(ostream&, MyType) auf jeden Fall schon erklären muss, bevor die erste eigene Ausgabe gemacht werden soll, insbesondere vor operator+ .

    Für dieses Beispiel erst den op<< bauen. Dann erst den op++ bauen. Das sagt doch nicht, daß man den op<< bauen sollte, bevor man einen op<< benutzt.
    Aber das Beispiel ist eh nicht gut gewählt.
    Eine gute Reihenfolge der Lehrinhalte zurechtzuschuggern ist schon mühsam. Aber da muß ein Autor durch.



  • EOutOfResources schrieb:

    Michael E. schrieb:

    [...]

    Er soll's lassen wenn er's nicht richtig macht...

    Tut mir Leid, aber das ist Blödsinn. Sobald Klassen eingeführt wurden, will ich Member ausgeben. Soll dann direkt operator<< überladen werden? Am besten noch bevor der Leser weiß, was public und private heißen.


Anmelden zum Antworten