C++ Operatorüberladung



  • Hallo,

    Ich wollte zu Übnungszwecken eine Klasse Bruch schreiben. Sie soll einen Bruch abbilden und mithilfe der Operatorüberladung auch rechnen können. Nun habe ich in zwei Büchern nachgesehen wie es geht und beide varianten zeigen dieselbe Methode die ich unten beschrieben habe, mein problem ist das es nicht funktioniert. Ich weiß das die Funktion noch nicht Mathematisch korrekt ist aber es geht hier erstmal darum das er sie ausführen soll.

    IDE = Codeblocks
    Compiler = MinGW

    Buch 1: Objektorientiert Programmieren mit ANSI C++ (Prentice Hall)
    Buch 2: C C++ (Kaiser Guddat (Gallileo Computing))

    Meine Bruch.h

    #ifndef BRUCH_HPP
    #define BRUCH_HPP
    
    class Bruch
    {
        private:
            float nenner;
            float zaehler;
        public:
            Bruch();
            Bruch(float zaehler, float nenner);
            float getNenner();
            float getZaehler();
            float setNenner(float nenner);
            float setZaehler(float zaheler);
    
            Bruch operator+(Bruch a, Bruch b);
    };
    
    #endif // Bruch_HPP
    

    Meine Bruch.cpp

    #include "Bruch.h"
    
    Bruch::Bruch()
    {
        zaehler = 0;
        nenner = 1;
    
    }
    
    Bruch::Bruch(float zaehler, float nenner)
    {
        this->nenner = nenner;
        this->zaehler = zaehler;
    }
    
    float Bruch::getNenner()
    {
        return nenner;
    }
    
    float Bruch::getZaehler()
    {
        return zaehler;
    }
    
    float Bruch::setNenner(float nenner)
    {
        this->nenner = nenner;
    }
    
    float Bruch::setZaehler(float Zaehler)
    {
        this->zaehler = zaehler;
    }
    
    Bruch::operator*(Bruch a, Bruch b)
    {
        Bruch c;
        c.setNenner( a.getNenner() * b.getNenner() );
        return c;
    }
    

    Compilerfehler:
    ||=== Build: Debug in Klasse Bruch (compiler: GNU GCC Compiler) ===|
    |17|error: 'Bruch Bruch::operator+(Bruch, Bruch)' must take either zero or one argument|
    ||In member function 'float Bruch::setNenner(float)':|
    |29|warning: no return statement in function returning non-void [-Wreturn-type]|
    ||In member function 'float Bruch::setZaehler(float)':|
    |34|warning: no return statement in function returning non-void [-Wreturn-type]|
    |36|error: 'int Bruch::operator*(Bruch, Bruch)' must take either zero or one argument|
    ||=== Build failed: 2 error(s), 2 warning(s) (0 minute(s), 0 second(s)) ===|

    Ich verstehe es einfach nicht, vorallem weil es in beiden Büchern genau so beschrieben wird.

    Ich wäre sehr dankbar wenn mir jemand helfen kann.

    Mit freundlichen Grüßen

    Theroth



  • Theroth schrieb:

    (Gallileo Computing)

    da kommt einem das gruseln. das andere buch kenne ich nicht, aber wenn dir das dieses beispiel auch zeigt, ist es ebenso falsch.

    im prinzip solltest du es so machen:

    class Bruch {
    //...
      friend Bruch operator+ (Bruch a, Bruch b) {
        return a += b;
      }
    
    public:
      Bruch& operator+=(const Bruch& b) {
         //calculate this += b
         return *this;
      }
    };
    

    - die freien operatoren immer* mithilfe der += operatoren implementieren.

    die fehlermeldung deines compilers lautet übersetzt so viel wie:
    "es gibt keinen operator+ mit zwei argumenten als elementfunktion"
    lösung: den operator+(a, b) nicht als elementfunktion, sondern als freie funktion (evtl. als "friend") definieren. als elementfunktion wäre nämlich das erste argument (implizit) "this" und ein Bruch::operator+(Bruch a, Bruch b) müsste irgendeine berechnung mit this, a und b anstellen - so einen "dreiwertigen" operator+ gibt es aber schlicht und einfach nicht.



  • Hallo.

    Paar Kommentare zum Code:

    Theroth schrieb:

    Meine Bruch.h

    #ifndef BRUCH_HPP // die Datei heißt Bruch.h, würde also auch den Include-Guard konsequenterweise so benennen
    #define BRUCH_HPP
    
    class Bruch
    {
        // ist per default private, brauchst du ergo nicht schreiben; kann ich aber verstehen, wenn du das explizit so verdeutlichen möchtest
        private:
            float nenner;
            float zaehler;
        public:
            Bruch();
            Bruch(float zaehler, float nenner);
            float getNenner(); // sollte const sein, weil sie *this nicht verändert
            float getZaehler(); // sollte const sein, weil sie *this nicht verändert
            float setNenner(float nenner);
            float setZaehler(float zaheler);
    
            Bruch operator+(Bruch a, Bruch b); // sollte nur ein Argument annehmen, denn das linke Argument der Addition ist *this
    };
    
    #endif // Bruch_HPP
    

    Meine Bruch.cpp

    #include "Bruch.h"
    
    Bruch::Bruch()
    {
        // wenn du C++11-Support hast, würde ich das mit einem delegating constructor lösen; ansonsten gleicher Tipp wie beim anderen Konstruktor
        zaehler = 0;
        nenner = 1;
    
    }
    
    Bruch::Bruch(float zaehler, float nenner)
    {
        // hier spielt es zwar keine Rolle, aber gewöhn dir an, solche Dinge mit einer member initializer list zu machen
        this->nenner = nenner;
        this->zaehler = zaehler;
    }
    
    float Bruch::getNenner() // const
    {
        return nenner;
    }
    
    float Bruch::getZaehler() // const
    {
        return zaehler;
    }
    
    float Bruch::setNenner(float nenner)
    {
        // return fehlt
        this->nenner = nenner;
    }
    
    float Bruch::setZaehler(float Zaehler)
    {
        // dito
        this->zaehler = zaehler;
    }
    
    // hier hast du einerseits den Rückgabetyp vergessen, andererseits hast du einen anderen (*) Operator-Typen als in der Deklaration (+) verwendet
    Bruch::operator*(Bruch a, Bruch b)
    {
        // Brüche multipliziert man ein bisschen anders ;)
        Bruch c;
        c.setNenner( a.getNenner() * b.getNenner() );
        return c;
    }
    

    dove schrieb:

    im prinzip solltest du es so machen:

    Sehe ich anders. Einerseits würde ich operator+ nicht als friend markieren, sondern als gewöhnlichen globalen Operator, und andererseits kannst du Bruch b hier als const-ref übergeben. Oder als Code:

    class Bruch
    {
    public:
    	Bruch& operator+= (Bruch const& Rhs)
    	{
    		// ...
    		return *this;
    	}
    };
    
    Bruch operator+ (Bruch Lhs, Bruch const& Rhs)
    {
    	return Lhs += Rhs;
    }
    

    Es gibt Ausnahmefälle, wo man zuerst operator+ definiert und dann mithilfe dessen den operator+= , aber die sind selten.



  • Für die Bücher solltest du ne schöne Altpapiertonne suchen. Das eine ist uralt, das andere verheißt mit C/C++ nichts gutes.


  • Mod

    asfdlol schrieb:

    dove schrieb:

    im prinzip solltest du es so machen:

    Sehe ich anders. Einerseits würde ich operator+ nicht als friend markieren, sondern als gewöhnlichen globalen Operator, und andererseits kannst du Bruch b hier als const-ref übergeben. Oder als Code:

    class Bruch
    {
    public:
    	Bruch& operator+= (Bruch const& Rhs)
    	{
    		// ...
    		return *this;
    	}
    };
    
    Bruch operator+ (Bruch Lhs, Bruch const& Rhs)
    {
    	return Lhs += Rhs;
    }
    

    Es gibt Ausnahmefälle, wo man zuerst operator+ definiert und dann mithilfe dessen den operator+= , aber die sind selten.

    Eine solche friend-Funktion ist eine gewöhnliche Namensraum-Funktion. Die fehlende const-Referenz des 2. Parameters in dove's Code dürfte einfachen Copy&Paste geschuldet sein. Und op+ wurde auch bei ihm bereits durch += implementiert. Was genau siehst du anders?



  • Abgesehen von den Argumenten passt da noch etwas nicht:

    Bruch operator+(Bruch a, Bruch b);
    
    Bruch::operator*(Bruch a, Bruch b)
    {
    // ...
    }
    

    Wieso sind Zähler und Nenner denn überhaupt float? Der Sinn eines Bruches ist doch gerade, dass man ganze Zahlen in Relation setzte?



  • camper schrieb:

    Eine solche friend-Funktion ist eine gewöhnliche Namensraum-Funktion.

    ..., die die Kapselung verringert. Gibt keinen Grund, wieso dieser Operator Zugriff auf die Privates haben sollte.


  • Mod

    asfdlol schrieb:

    Gibt keinen Grund, wieso dieser Operator Zugriff auf die Privates haben sollte.

    Gibt keinen Grund, wieso das eine Rolle spielen sollte. Zumal von diesen Zugriffsrechten kein Gebrauch gemacht wird.



  • Noch ein paar Anmerkungen: Den this pointer muss man in member-funktion-definitionen nicht explizit angeben, um auf member zuzugreifen.

    Es ist außerdem eine schlechte Idee, Namen nur durch Groß-/Kleinschreibung zu unterscheiden. Das ist schwer lesbar und kann außerdem böse bugs herbeiführen:

    float Bruch::setZaehler(float Zaehler)  // <---
    {
        this->zaehler = zaehler;  // <---
        // zaehler = zaehler;
    }
    

    Dann gibt es natürlich immer das Dilemma, dass die Parameter gleich benannt werden wie die Member, manche machen dann ein m bzw m_ oder sonstiges Zeug, um die Parameter von den Membern zu unterscheiden. Persönlich finde ich, dass man bei der Definition auch einfach den Namen abkürzen kann, vor allem bei so kurzen Funktionen. Warum die set-Methode einen Wert zurückgibt ist mir auch ein Rästel. Noch schlimmer, wenn dann das return fehlt. Da sollte der Compiler meckern:

    /* float */ void Bruch::setZaehler(float z)
    {
        zaehler = z;
    }
    
    void Bruch::setZaehler(float neuerZaehler)
    {
        zaehler = neuerZaehler;
    }
    


  • Vielen dank für die Hilfe, hat mir echt geholfen es funktioniert jetzt auch wieder alles.

    Da ich neu bin, weiß ich nicht ob das Thema geschlossen oder auf gelöst gesetzt werden kann.

    Mit freundlichen Grüßen

    Theroth


Anmelden zum Antworten