Operatoren überladen in Klassen



  • hi,

    ich arbeite gerade ein wenig für ein projekt an der fh...aufgabe ist eine FIFO Container-Struktur zum auslesen von Strings...

    dabei möchte ich einen String auslesen und diesen in einer Methode Pop() in die Klasse einlesen...

    das soll über überladen des Operators String funktionieren...hier ein Teil meiner Klassendefinition:

    public:
            FifoClass() : top(0) {}
            //FifoClass(const char*);
            ~FifoClass() { delete top; }
    
            FifoClass& operator << (const string& s) { return push(s); }
            FifoClass& operator >> (string& s) { return pop(s); }
            **inline operator string() { return pop(); }**
            operator int () const { return elements; }
            //const char* Error() const;
    
    ...
    
    FifoClass& FifoClass:pop(string &s) {
        s = *top;
        FifoEl *help=top;
        top = top->next_element();
        delete help;
        return *this;
    }
    

    Vor die Problemstelle hab ich mal zwei Sterne gemacht. Mein Compiler (Dev C++) gibt mir folgende Fehlermeldung an dieser Stelle:

    FifoClass.h no matching function for call to `FifoClass::pop()'
    candidates are: FifoClass& FifoClass::pop(std::string&)

    wieso klappt das nicht so und wie kann ich das geschickt lösen? danke für die hilfe...



  • **inline operator string() { return pop(); }**
    

    - hinter dem operator fehlt ein operator
    - pop erwartet einen string
    - es fehlt ein Rückgabewert
    - dürfen operatoren inline sein?
    - das string() hat da nichts zu suchen



  • pop erwartet einen string.
    Davon ab, using in Headern ist nicht sehr schick.

    edit @ Ixtana: Schlag mal "Konvertierungsoperator" o.s.ä. nach 😉



  • ja, hab ich mir auch schon gedacht! in folgendem beispiel läuft es aber:

    #include "stack6.h"
    #include<iostream>
    using namespace std;
    
    int main() {
    try {
     StackClass s; // Stack einrichten
     s << 100 << 101 << 102; // 3 Elemente auf Stack
     for (int i=0;i<10;i++) {
       s << i;
     }
     for (int i=0;i<10;i++) {
       cout << s << ", ";
     }
     cout << s << ", " << s << ", " << s << endl;
     } catch (...) { cout << "Ausnahme" << endl; return 3; }
     return 0;
    }
    
    #ifndef _P1_h_
    #define _P1_h_
    
    #include <string>
    #include <iostream>
    #include <fstream>
    #include <stdlib.h>
    
    using namespace std;
    
    class StackClass {
      class StackElement {
    StackElement* next;
    int value;
    public:
    StackElement(int v, StackElement* n) : value(v), next(n) {};
    ~StackElement() { delete next; }
    inline StackElement* next_element()
    { StackElement *t=next; next=0; return t; }
    inline operator int () { return value; };
    };
    StackElement *top;
    StackClass& push (int v);
    StackClass& pop (int &v);
    public:
    StackClass () : top(0) {}
    ~StackClass () { delete top; }
    **inline operator int () { return pop(); }**
    inline int pop () { int v; pop (v); return v;}
    inline bool empty() { return 0==top; }
    inline StackClass& operator << (int v) { return push(v); }
    inline StackClass& operator >> (int &v) { return pop(v); }
    };
    
    StackClass& StackClass::push (int v) {
    top = new StackElement(v, top);
    return *this;
    }
    
    StackClass& StackClass::pop (int &v) {
     if (empty()) throw "Stack underflow";
     v = *top; // Wert aus erstem Element lesen
     StackElement *help=top; // Zeiger auf erstes Element retten
     top = top->next_element(); // top auf zweites Element setzen
     delete help; // erstes Element löschen
     return *this; // Referenz auf Stack zurückgeben
    }
    #endif
    

    auch hier wieder die überladene sache mit ** markiert! warum läuft das denn bei den strings nicht? hab das fast 1 zu 1 übernommen?



  • Ich hoffe du kennst die Nachteile, die implizite Casts mitsich bringen.

    So ist zum Beispiel folgender Code, nicht gerade
    logisch nachvollziehbar, kompiliert aber dennoch:

    void g(const string& str) {
        StackClass s;
    
        // ...
    
        if(s == 5) {
            // mach dies ..
        } else if(s == str) {
            // .. oder jenes ..
        }
    
        // ...
    }
    

    Ich hoffe du verstehst, was ich mein.

    Wirst wahrscheinlich vor kurzem Operatoren Überladung durchgenommen haben,
    da wirkt das natürlich höchst professionell. Allerdings solltest du wirklich
    zuerst 3mal überlegen, ob der Operator wirklich den Sinn wiedergibt.

    Folgender Code würde auch für andere Programmierer sofort verstanden werden:

    void g(const string& str) {
        Stack s;
    
        s.push(100);
        s.push(101);
        s.push(102);
    
        // ..
    
        if(s.getCount() == 5) {
            // mach dies ..
        } else if(s.pop() == str) {
            // .. oder jenes ..
        }
    }
    

    Außerdem warum gibst du bei pop() Stack& zurück. Ich mein mir ist klar,
    dass man damit Aufrufe verketten kann, aber damit verwirfst du ja alle
    Ergebnisse ..

    Außerdem wirft man als Exceptions keine strings, da definiert man sich
    eine benutzerdefinierte Exception oder benutzt vordefinierte, aber
    jeder Typ den du wirfst, sollte vom Typ std::exception erben.

    grüße



  • tja, ganz ehrlich, ich würde an die sache auch anders rangehen! jedoch ist uns ein teil der klasse vorgegeben worden. halt auch die überladung vom string! ansonsetn läuft das programm, nur diese überladung halt nicht! ich werd dann morgen einfach mal meinen proff anmotzen, dass das doof ist 🙂



  • Der << Operator ist nicht unär sondern binär. In C++ definiert man binäre Operatoren als globale Funktionen.

    void operator<<(ostream& output, const Foo& object);
    

    Irgendwie so.



  • Naja wenn die ganzen Signaturen vorgegeben sind, dann ist das was
    anderes. Kenne solche Dummheiten von meinem Prof.

    inline operator string() {
        string temp;
        pop(temp);
        return temp;
    }
    

    grüße



  • Optimizer schrieb:

    void operator<<(ostream& output, const Foo& object);
    

    Irgendwie so.

    ostream& operator<<(ostream& output, const Foo& object);
    

    😉



  • Implizit schrieb:

    Naja wenn die ganzen Signaturen vorgegeben sind, dann ist das was
    anderes. Kenne solche Dummheiten von meinem Prof.

    inline operator string() {
        string temp;
        pop(temp);
        return temp;
    }
    

    grüße

    oh super 🙂 so läuft es sogar! ich bin zu tiefsten dank verpflichtet 🙂 und mein proff bekommt morgen ärger von mir 🙂 bis jetzt waren seine vorgaben immer unglaublich hilfreich in diesem fall eher störend, ansonsten wäre das programm wohl schon fertig...



  • wieso ist keinem aufgefallen das
    [cpp]
    FifoClass& FifoClass:pop(string &s) {
    [/cpp]
    ein FifoClass& zurückgibt und das der operator

    inline operator string() { return pop(); }
    

    einen string zurückgeben sollte ? 😕
    Hoeen bist echt ein spassvogel. return pop(); haha. pop() erwartet als parameter einen string
    ok finix hatte das schon 🕶

    und die zwei operatoren

    FifoClass& operator << (const string& s) { return push(s); }
    FifoClass& operator >> (string& s) { return pop(s); }
    

    haben doch nix mit

    ostream& operator<<(ostream& output, const Foo& object);
    

    zu tun.
    der letztere ist für streams gedacht.
    die beiden oberen doch für sowas:

    FifoClass fifo;
    fifo << "blaa" << "xxx";
    


  • DEvent schrieb:

    ...und das der operator

    inline operator string() { return pop(); }
    

    einen string zurückgeben sollte ? 😕
    Hoeen bist echt ein spassvogel. return pop(); haha. pop() erwartet als parameter einen string

    ja stimmt, aber ich war etwas verwundert, weil es in folgendem beispiel halt klappt:

    inline operator int () { return pop(); }
    
    StackClass& StackClass::pop (int &v) {
     if (empty()) throw "Stack underflow";
     v = *top; // Wert aus erstem Element lesen
     StackElement *help=top; // Zeiger auf erstes Element retten
     top = top->next_element(); // top auf zweites Element setzen
     delete help; // erstes Element löschen
     return *this; // Referenz auf Stack zurückgeben
    }
    

    und hier erwartet pop() auch einen parameter...vielleicht kann mir das ja einer erklären?!?

    DEvent schrieb:

    und die zwei operatoren

    FifoClass& operator << (const string& s) { return push(s); }
    FifoClass& operator >> (string& s) { return pop(s); }
    

    haben doch nix mit

    ostream& operator<<(ostream& output, const Foo& object);
    

    zu tun.
    der letztere ist für streams gedacht.
    die beiden oberen doch für sowas:

    FifoClass fifo;
    fifo << "blaa" << "xxx";
    

    daher bin ich da auch nicht drauf eingegangen, da es für mich uninteressant ist. wie gesagt, es geht nicht um irgendwelche streams...ein teil der main sieht z.b. so aus...

    cout << "> Fifo aus Textdatei fuellen" << endl;
        FifoClass s( (argc>1) ? argv[1] : "test.txt" ); // Fifo einrichten und aus Textdatei füllen
        cout << "> Fifo ausgeben" << endl;
        while (s>0) {                           // Elemente ausgeben
          cout << static_cast<string>(s) << endl;
        }
        cout << "> Fifo mittels Ein- und Ausgabeoperator fuellen" << endl;
        s << "One"; s << "Two"; s << "Three";
        cout << "> Fifo ausgeben" << endl;
        string help;
        s >> help; cout << help << endl;
        s >> help; cout << help << endl;
        s >> help; cout << help << endl;
    

    die ausgabe funktioniert auch! leider habe ich ein LIFO und kein FIFO wie in der aufgabe erwartet, aber das kommt heute noch 🙂



  • da es ja zum guten stil gehört, auch die lösungen anzugeben, hier meine lösung zur fifo ein und ausgabe...

    //Definition der Klassen
    
    #ifndef _P3_h_
    #define _P3_h_
    
    #include <string>
    #include <iostream>
    #include <fstream>
    #include <stdlib.h>
    
    using namespace std;
    
    class FifoEl{
      FifoEl* next;
      string s;
      public:
          FifoEl(string el, FifoEl* n) : s(el), next(n) {};
          ~FifoEl() {}
          inline operator string () { return s; };
      friend class FifoClass;
    };    
    
    class FifoClass
    {  
      FifoEl *first;
      FifoEl *end;
    
      public:      
          FifoClass() : first (0) {}          
          FifoClass(const char* dat) 
          {
              count = 0;
              ifstream ein;
              ein.open (dat, ifstream::in);
              if(!ein) cout << "Konnte Datei nicht oeffnen."; 
              int start (0), ende (0);
              string Zeile(""), PushString("");
    
              while(!ein.eof())
              {
                  Zeile = "";
                  getline(ein, Zeile, '\n');
                  push(Zeile);   
              }    
          } 
    
          ~FifoClass() {delete first; }
          FifoClass& operator<<(const string& s) {return push(s);}
          FifoClass& operator>>(string& s) {return pop(s);}
          inline operator string() {
              string temp;
              pop(temp);
              return temp;
          }
          operator int() const {return count;}
          const char* Error() const 
          {
               cout << "HIER FEHLER" << endl;
          } 
          inline bool empty() {return 0 == count; }
          int count;
    
      private:
          string s;   
          FifoClass& push(string s);
          FifoClass& pop(string &s);
    };
    
    FifoClass& FifoClass::push (string s) {
        if(count == 0)
        {
            first = new FifoEl(s, first);
            end = first;
            end->next = NULL;
        }
        else {
            end->next = new FifoEl(s, end);
            end = end->next;
            end->next = NULL;
        }  
        count++;  
        return *this;
    }
    
    FifoClass& FifoClass::pop(string &s) {
        if(empty()) throw "Stack underflow";
        s = *first;
        FifoEl *help=first;
        first = first->next;
        delete help;
        count--;
        return *this;
    }
    

    es wurden zwei zeiger angelegt. einer auf das erste und einer auf das letzte element...elemente werden hinten angefügt. gelesen wird jedoch von vorne...


Anmelden zum Antworten