Präfix-/Postfix-Operatoren überladen



  • Ich habe folgendes Problem zu lösen:

    Schreiben Sie die drei Klassen Objekt, Liste und Iterator, die folgende Funktionalität bieten sollen: Objekt ist ein Stellvertreter für eine beliebige Klasse. Diese Klasse sollte für Testzwecke zumindest eine print Methode aufweisen, die eine eindeutig Identifizierung der Objekte ermöglicht. Liste soll Objekte in einem Array verwalten. Die Liste soll die Methoden add und insert aufweisen, die ein Objekt am Ende bzw. am Beginn der Liste einfügen können. Iterator soll einen Konstruktor haben, der den Iterator an den Beginn der Liste setzt, die Operatoren ++ und --, die zum Weiter- bzw. Zurückgehen in der Liste dienen und eine Methode obj, die das aktuelle Objekt retourniert. Unter der Annahme, dass die Liste mit den vier Objekten 'a', 'b', 'c' und 'd' gefüllt ist, soll z.B. folgendes Programmstück die angeführten Ergebnisse liefern:

    Iterator it;
    it.obj().print(); //Ausgabe von 'a'
    it++;
    it.obj().print(); //Ausgabe von 'b'
    it--;
    it.obj().print(); //Ausgabe von 'a'

    (soweit ist alles klar; der gesamte Code läuft einwandfrei)

    Ändern Sie die Klasse Iterator nun so, dass die Methode obj überflüssig wird. Statt dessen soll der Operator * (Dereferenzierung) und die Operatoren ++ und -- derart überladen werden, dass das kurze Programmstück im Beispiel aus der Vorwoche nun folgendermaßen geschrieben werden kann:
    Iterator it;

    **(*it++).print(); //Ausgabe von 'a'
    (it--).print(); //Ausgabe von 'b'
    (it).print(); //Ausgabe von 'a'

    Die Deklarationen von Objekt & Liste sehen folgendermaßen aus:
    (Methode 'obj' heißt hier 'Member'; außerdem nimmt Objekt Daten vom typ int auf)

    const int iSize=128;
    
    //Demo-Klasse für den Listeniterator
    //Objekte werden in eine Liste aufgenommen
    class Objekt
    {
      private:
        int iDaten;
    
      public:
    
    //KONSTRUKTOR
    
        Objekt (int Daten=-1);
    
    //METHODEN
        //Gleich wie Konstruktor
        void setValue (int Daten);
    
        //Objektzahl ausgeben
        void print ();
    };
    
    //Auf ihr operiert der Iterator
    class Liste
    {
      private:
        //Array an Objekten
        Objekt oObjekte[iSize];
    
        //Höchster Index im Array
        int MaxIndex;
    
      public:
    
    //KONSTRUKTOREN
    
        //Leere Liste
        Liste ();
        //Übernimmt ein Array an Objekten & dessen Größe (Überlauf!)
        Liste (Objekt Objekte[iSize], int ActualSize);
    //METHODEN
        //Objekt am Ende einfügen
        bool addEnd (Objekt oInput);
        //Objekt am Beginn einfügen    
        bool insertFirst (Objekt oInput);
    
        //Anzahl der Objekte im Array
        int count ();
        //Ein einzelnes Objekt
        Objekt Member (int Index);
    };
    

    Ich habe die entsprechenden Operatoren der Klasse Iterator nun folgendermaßen definiert:

    //Setzt den Iterator ans nächste Listenelement
        //Beginnt von vorne wenn am Ende    
        void Iterator::operator++ (int)
        {    
             iAktuelleStelle = (iAktuelleStelle == lListe.count()-1 ? 0 : ++(iAktuelleStelle));
             //return operator*();         
        }
    
        //Setzt den Iterator ans vorige Listenelement
        //Beginnt von hinten wenn am Anfang
        void Iterator::operator-- (int)
        {
             iAktuelleStelle = (iAktuelleStelle == 0 ? lListe.count()-1 : --(iAktuelleStelle));         
             //return operator*();                  
        }
    
        //Retourniert das Objekt an der aktuelle Position
        Objekt Iterator::operator* () 
        {
             return lListe.Member (iAktuelleStelle);
        }
    

    Der Operatoraufruf funktioniert problemlos, solange ich nur jeweils einen Operator verwende:

    cout << "\nWert an voriger Stelle: "; (*it).print(); (it++); //richtige Ausgabe
    cout << "\nWert an aktueller Stelle: "; (*it).print(); //richtige Ausgabe
    

    Problematisch wirds allerdings hier:

    (*it++).print();
    

    Compilerfehler: 'void value not ignored as it ought to be'

    Ändere ich nun die Ergebnistypen in Iterator entsprechend:

    //Setzt den Iterator ans nächste Listenelement
        //Beginnt von vorne wenn am Ende    
        Objekt Iterator::operator++ (int)
        {    
             iAktuelleStelle = (iAktuelleStelle == lListe.count()-1 ? 0 : ++(iAktuelleStelle));
             return operator*();         
        }
    
        //Setzt den Iterator ans vorige Listenelement
        //Beginnt von hinten wenn am Anfang
        Objekt Iterator::operator-- (int)
        {
             iAktuelleStelle = (iAktuelleStelle == 0 ? lListe.count()-1 : --(iAktuelleStelle));         
             return operator*();                  
        }
    
        //Retourniert das Objekt an der aktuelle Position
        Objekt Iterator::operator* () 
        {
             return lListe.Member (iAktuelleStelle);
        }
    

    Erhalte ich stattdessen die Meldung:
    'no match for 'operator' in '*(&it)->Iterator::operator++(0)''*

    Ich habe zwar Vermutungen, wie die beiden Meldungen zu interpretieren sein könnten, aber bis jetzt keine Lösung gefunden.
    Bin daher für jeden Lösungsansatz dankbar!



  • Wenn du die Rückgabe von op++ dereferenzieren willst, mußt du dort einen sinnvollen Rückgabewert angeben:

    üblicherweise sehen die Inkrement-Operatoren übrigens so aus:

    Typ& operator++()
    {
      //Präfix-Inkrement ++x
      return *this;
    }
    
    Typ operator++(int)
    {
      Typ tmp=*this;
      ++this;
      return tmp;
    }
    


  • Eigentlich sollte der operator++(int) eine Kopie von Iterator zurückgeben.

    Der Postfix operator++ kann übrigens ruhig ein konstantes Objekt zurückgeben:

    const Iterator Iterator::operator++( int )
    {
      Iterator tmp( *this );
      // ...
      return tmp;
    }
    

    Edit:
    Bei deinem Dereferenzierungsoperator wäre eine konstante Referenz als Rückgabetyp besser.

    const Objekt& operator*() const
    {
    }
    

    grüße


  • Mod

    David_pb schrieb:

    Bei deinem Dereferenzierungsoperator wäre eine konstante Referenz als Rückgabetyp besser.

    const Objekt& operator*() const
    {
    }
    

    grüße

    Das hängt davon ab, ob die Konstanz des Iterators die Konstanz des Objekts zur Folge haben soll. Für viele (in meiner Erfahrung die meisten) Iteratoren und Smartpointer gilt das nicht. Auch für den normalen Pointer als Prototyp des Iterators ist das ja nicht der Fall. Richtig ist aber, dass der Operator selbst const sein sollte.



  • So wie Christian++ den Operator deklariert hat, hätte ein ändern des Objekts garkeinen Erfolg im Bezug auf das Objekt innerhalb des Iterators. Ansonsten soll er eben eine nicht konstante Referenz zurückgeben.



  • Wahnsinn, dieses Forum... 😃
    Herzlichen Dank an Euch alle!


Anmelden zum Antworten