Überladen von Operatoren



  • Hi,

    ich hab ein kleines Problem beim Überladen von den Standartoperatoren.

    emx Term alpha("5+3-(23+4)");
    emx Term beta("3-4*(a-3/(3+a))");
    ...
    beta = alpha + beta;
    ...
    cout << "Ausgabe: "     << endl;
    cout << alpha.getTerm() << endl;
    cout << beta.getTerm() << endl;
    
    Ausgabe:
    (5+3-(23+4))+(3-4*(a-3/(3+a)))
    (5+3-(23+4))+(3-4*(a-3/(3+a)))
    

    Wie ihr seht bekommt Alpha den gleichen Wert wie Beta, was eigentlich nicht passieren sollte. Hier ist der Code dazu. Ich weiß nicht ob es anders geht oder ob es wirklich immer so ist, da ja sozusagen alpha.operator+(beta) steht. Weiß jmd wie ich das verhindern kann. Also Alpha sozusagen nicht zu überschreiben, ohne einfach alpha mit beta oben zu vertauschen.

    Ich seh das Problem leider aber ich weiß nicht wie ich oder ob ich es verhindern kann.

    exmTerm &exmTerm::operation(const exmTerm& c_operand,char c_operator){
      char *dummy;
      char *dummy2 = c_operand.get_start_term();
    
      int nu_length  = 1+(start_length)+1+1+1+(c_operand.get_start_length())+1;
    
      dummy = new char[nu_length+1];
      *(dummy+nu_length) = '\0';
      *(dummy+0) = '(';
    
      int i = 0;
      for(;'\0'!=*(start_term+i);i++)
        *(dummy+i+1) = *(start_term+i);
    
      i++;
      *(dummy+i) = ')'; i++;
      *(dummy+i) = c_operator; i++;
      *(dummy+i) = '('; i++;
    
      for(int j=0;'\0'!=*(dummy2+j);j++,i++)
        *(dummy+i) = *(dummy2+j);
    
      *(dummy+i) = ')';
    
      if(1<start_length)
        delete[] start_term;
      else
        delete start_term;
    
      start_term    = dummy;
      start_length  = nu_length;
    
      return *this;
    }
    
    exmTerm &exmTerm::operator=(const exmTerm &nu_term){
      set_basic();
    
      char *dummy = nu_term.get_start_term();
    
      for(start_length = 0;'\0'!=*(dummy+start_length);start_length++);
      start_term = new char[start_length+1];
      *(start_term+start_length) = '\0';
    
      for(int i = 0;'\0'!=*(start_term+i);i++)
        *(start_term+i) = *(dummy+i);
    
      failure = !isTermOk();
    
      return *this;
    }
    
    exmTerm &exmTerm::operator+(const exmTerm& c_operand){
      return operation(c_operand,'+');
    }
    exmTerm &exmTerm::operator+=(const exmTerm& c_operand){
      return operation(c_operand,'+');
    }
    ...
    


  • Um das Problem ein bisschen näher zu erleutern.

    exmTerm alpha("5+3-(23+4)")
    exmTerm beta("3-4*(a-3/(3+a)")
    exmTerm gamma("2-2")
    
    beta = beta + alpha + gamma;
    
    cout << alpha.getTerm() << endl;
    cout << beta.getTerm() << endl;
    cout << gamma.getTerm() << endl;
    

    Ausgabe:

    5+3-(23+4)
    (5+3-(23+4))+(3-4*(a-3/(3+a))+(2-2)
    2-2
    
    exmTerm alpha("5+3-(23+4)")
    exmTerm beta("3-4*(a-3/(3+a)")
    exmTerm gamma("2-2")
    
    beta = alpha + beta + gamma;
    
    cout << alpha.getTerm() << endl;
    cout << beta.getTerm() << endl;
    cout << gamma.getTerm() << endl;
    

    Ausgabe:

    (5+3-(23+4))+(3-4*(a-3/(3+a))+(2-2)
    (5+3-(23+4))+(3-4*(a-3/(3+a))+(2-2)
    2-2
    

    Wie ihr seht... der erste Operand in der Reihe beim ersten Beta, beim zweiten Alpha wird überschrieben. Was ich eigentlich nicht will... könnte man das in irgendeinerweise endern.



  • Ohne genau deinen Code studiert zu haben, aber der Operator+ sollte keinen Verweis zurück geben, statt dessen würde ich den Operator extern machen (das ist aber eher optional), auf keinen Fall aber ohne const als Rückgabewert.

    Meine mögliche Operator-Signatur:

    const exmTerm exmTerm::operator+(const exmTerm& c_operand){
      return operation(c_operand,'+');
    }
    exmTerm& exmTerm::operator+=(const exmTerm& c_operand){ 
      return operation(c_operand,'+'); // Zuweisungs-Operator! Sicher das dass so richtig ist?
                                       // Es fehlt noch die Prüfung auf Selbstzuweisung.
    }
    

    Wie gesagt ich habe mir deinen Code nicht genau angesehen, sieht mir zu verwirrend aus, sorry.
    Es gilt aber: "Gib keine Verweise zurück, wenn du ein Objekt zurückgeben musst."

    G-DC!



  • Hallo Razor!
    - get_start_term scheint eine const-Elementfunktion zu sein, die einen char* auf ein internes Array zurückgibt. (Pfui! bzgl Const-Correctness)
    - Du machst es Dir unnötig kompliziert. Benutze std::string. Damit wird vieles einfacher (Du brauchst keinen eigenen Copy-Ctor, Destruktor, Zuweisungsoperator mehr) und entsprechend auch leserlicher.
    - Du unterscheidest + nicht von +=. Der + Operator verändert seine Argumente nicht! Er liefert einen neuen Wert zurück (und keine Referenz auf ein verändertes Argument)

    DeepCopy schrieb:

    [...] auf keinen Fall aber ohne const als Rückgabewert.

    Das ist eher Geschmackssache. Ich finde, das sollte man sich spätestens jetzt, wo C++0x vor der Tür steht, abgewöhnen. (Siehe rvalue references, move semantics)

    Gruß,
    SP



  • Danke ihr habt mir sehr geholfen. Jetzt nach ein wenig Codeaktualisierung funktioniert es ohne Probleme. 😃

    exmTerm &exmTerm::operation(const exmTerm& c_operand,char c_operator){
      //Hier wird der neue Hauptterm aus den beiden alten gebildet.
      [...]
      return *this;
    exmTerm &exmTerm::operator=(const exmTerm &nu_term){
      if(this!=&nu_term){
        set_basic();
    
        char *dummy = nu_term.get_start_term();
    
        for(start_length = 0;'\0'!=*(dummy+start_length);start_length++);
        start_term = new char[start_length+1];
        *(start_term+start_length) = '\0';
    
        for(int i = 0;'\0'!=*(start_term+i);i++)
          *(start_term+i) = *(dummy+i);
    
        failure = !isTermOk();
      }
    
      return *this;//dummy;
    }
    exmTerm &exmTerm::operator+=(const exmTerm& c_operand){
      if(this==&c_operand)
        return *this;
    
      return operation(c_operand,'+');
    }
    exmTerm exmTerm::operator+(const exmTerm& c_operand) const{
      exmTerm temp(*this);
      temp += c_operand;
      return temp;
    }
    

    Zu den Einwänden:

    Du machst es Dir unnötig kompliziert. Benutze std::string. Damit wird vieles einfacher (Du brauchst keinen eigenen Copy-Ctor, Destruktor, Zuweisungsoperator mehr) und entsprechend auch leserlicher.

    Ich wollte std::string zur übung mit Zeichenketten extra nicht benutzen.

    get_start_term scheint eine const-Elementfunktion zu sein, die einen char* auf ein internes Array zurückgibt. (Pfui! bzgl Const-Correctness)

    Wie könnte ich das besser machen? Ich hab tatsächlich einen interenen Pointer der zurückgegeben wird. Sollte ich ihn auch noch Const machen?



  • bzgl. Operatorüberladung gibts auch einen Artikel 😉 Und zwar genau weil Fragen wie deine immer wieder hier auftauchen...

    Razor schrieb:

    Ich wollte std::string zur übung mit Zeichenketten extra nicht benutzen.

    Über eine Sache zur Zeit, nicht alles auf einmal. Also entweder Operatoren oder Char-Arrays.



  • bzgl. Operatorüberladung gibts auch einen Artikel 😉 Und zwar genau weil Fragen wie deine immer wieder hier auftauchen...

    Wirklich ich meinte ich hätte nach sowas gesucht. Gleich nochmal suchen.

    Über eine Sache zur Zeit, nicht alles auf einmal. Also entweder Operatoren oder Char-Arrays.

    Gerade das hat mich gereizt, da man hier zusätzlich dynamischen Speicher anfordern muss... vor allem weil es so abstrakt ist 🙂

    nochmal thx 😃



  • Razor schrieb:

    bzgl. Operatorüberladung gibts auch einen Artikel 😉 Und zwar genau weil Fragen wie deine immer wieder hier auftauchen...

    Wirklich ich meinte ich hätte nach sowas gesucht. Gleich nochmal suchen.

    Januar-Ausgabe 2009 IIRC.

    Razor schrieb:

    Über eine Sache zur Zeit, nicht alles auf einmal. Also entweder Operatoren oder Char-Arrays.

    Gerade das hat mich gereizt, da man hier zusätzlich dynamischen Speicher anfordern muss... vor allem weil es so abstrakt ist 🙂

    Dann implementiere es erstmal mit Strings, bis die Geschichte mit den Operatoren richtig steht. Danach stellst du um auf charrays mit dynamischer Speicherverwaltung, zum Üben. So macht man das eigentlich normalerweise: die komplizierten/unbekannteren Dinge eines nach dem Anderen angehen, sonst weiß man manchmal nicht ob der Fehler den man gerade sucht nun in den Operatorüberladungen oder im charray-gefrickel steckt.



  • Werd ich demnächst machen, obwohl meiner meinung nach Chararrays nicht wirklich schwer oder unverständlich sind.



  • ...nicht wirklich schwer oder unverständlich...

    Noch nicht. Wenn Du Dir diesen Stil aber nicht abgewöhnst, wirst Du selbst nach 4 Wochen Pause durch ein eigenes, größeres Projekt nicht mehr durchsteigen.

    Kapselung und Abstraktion sind feine Sachen! Nutze sie.

    Du kannst zur Übung ja versuchen, Dir eine eigene String-Klasse zu erstellen.

    Gruß,
    SP



  • Eine eigene Stringklasse hört sich zwar gut an... aber ich wüsste jetzt nicht auf annhieb, wie ich einen einzelen Char von einer richtigen Zeichenkette mit Nullterminierungszeichen unterscheiden soll.

    ala

    char a      = 'a';
    char *ptr_a = &a;
    

    Wie kann man herausfinden ob es nur ein Zeichen ist oder doch eine "kryptische" Zeichenkette.



  • Razor schrieb:

    Wie kann man herausfinden ob es nur ein Zeichen ist oder doch eine "kryptische" Zeichenkette.

    wenn man nur den pointer hat, dann gar nicht
    allerdings sollte deine eigene string-klasse ja die länge mitspeichern usw - das problem sollte sich dann also erübrigen...

    bb



  • Mach einfach immer eine Nullterminierung, selbst wenn 0 Zeichen gespeichert sind. Sonst bist du nur dauernd dran, Spezialfälle auszuarbeiten. Wegen eines Bytes solltest du dir keine Sorgen machen.

    Eine Stringklasse ist zweifelsohne gut zur Übung, allerdings empfehle ich dir dennoch, später std::string zu verwenden. Da sind sehr viele Dinge effizient implementiert und ausführlich getestet, du kannst dir unter Umständen mühsames Debugging ersparen. Besonders gut ist es, wenn du das Interface ähnlich zu std::string gestaltest (du musst ja selber nicht über 100 Funktionen implementieren), sodass du schnell auswechseln kannst. Also wichtige Dinge wie size() , c_str() etc.



  • Mach einfach immer eine Nullterminierung, selbst wenn 0 Zeichen gespeichert sind. Sonst bist du nur dauernd dran, Spezialfälle auszuarbeiten. Wegen eines Bytes solltest du dir keine Sorgen machen.

    Eine Stringklasse ist zweifelsohne gut zur Übung, allerdings empfehle ich dir dennoch, später std::string zu verwenden. Da sind sehr viele Dinge effizient implementiert und ausführlich getestet, du kannst dir unter Umständen mühsames Debugging ersparen. Besonders gut ist es, wenn du das Interface ähnlich zu std::string gestaltest (du musst ja selber nicht über 100 Funktionen implementieren), sodass du schnell auswechseln kannst. Also wichtige Dinge wie size(), c_str() etc.

    Das Problem liegt nicht direkt an einem internen Nullterminierungszeichen. Ich werde innerhlab eh keines benutzen und vllt eher mit 2 Zeigern arbeiten.

    Doch selbst std::string hat einige Probleme, was man aber nicht verhindern kann:

    char a  = '#';
    char *b = &a;
    
    std::string(b);
    
    std::cout << string << std::endl;
    
    //Die Wahrscheinlichkeit, dass nur '#' ausgegeben wird ist ziemlich gering.
    

    Deswegen wollt ich wissen, ob man auch ohne Nullterminierungszeichen die Länge herausfinden kann. Aber anscheinend geht es wirklich nicht.


Log in to reply