Operator Overloading



  • Hallo,

    Übe mich momentan im Operator Overloading, dazu wollte ich mal ne Bruch-Klasse schreiben, doch schon bei der Multiplikation von Brüchen wirft mein Compiler einen Error:

    invalid operands of types `Bruch*' and `Bruch*' to binary `operator*'
    

    Hier ist mein Code:

    #include <iostream>
    using namespace std;
    
    class Bruch {
          public:
                 Bruch(int zaehler = 1, int nenner = 1) : m_zaehler(zaehler), m_nenner(nenner) {}
                 void Display() const {
                      cout << m_zaehler << "/" << m_nenner << endl;
                 }
                 int GetZaehler() const { return m_zaehler; }
                 int GetNenner() const { return m_nenner; }
                 Bruch* operator*(Bruch* otherBruch) {
                       int new_zaehler, new_nenner;
                       new_nenner = otherBruch->GetNenner() * this->GetNenner();
                       new_zaehler = otherBruch->GetZaehler() * this->GetZaehler();
                       Bruch* bruch = new Bruch(new_zaehler, new_nenner);
                       return bruch;
                 }  
          private:
                  int m_zaehler, m_nenner;
    };
    
    int main() {
        Bruch* bruch = new Bruch(3, 5);
        Bruch* bruch_2 = new Bruch(3, 5);
        Bruch* bruch_3 = bruch * bruch_2;
    
        int x;
        cin >> x;
        return 0;
    }
    

    Freue mich über jede Hilfe und bedankte mich schonmal im Voraus.

    Achja, ist das was ich mache eigentlich effizient? Oder sollte man das ganze anderst lösen?



  • int main() {
        Bruch* bruch = new Bruch(3, 5);
        Bruch* bruch_2 = new Bruch(3, 5);
        Bruch* bruch_3 = bruch * bruch_2;
    
        int x;
        cin >> x;
        return 0;
    }
    

    Weil

    bruch * bruch_2
    

    dem Funktionsaufruf

    bruch.operator*(bruch_2)
    

    entspricht, und bruch nunmal nur ein Bruch* ist (also ein Zeiger).
    In Operatoren solltest du eh const-Referenzen übergeben, und der return sollte auch ein Objekt sein (also weder Referenz noch Zeiger), damit später auch mal sowas möglich ist:

    bruch4 = bruch1 * bruch2 * bruch3;
    

    Das geht mit deiner jetzigen Lösung nie und nimmer....



  • So würde dein Code laufen:

    Bruch* bruch_3 = bruch->operator*(bruch_2);
    
    // oder offensichtilicher:
    
    Bruch* bruch_3 = *bruch * bruch_2;
    

    Unschön! Referenzen benutzen!



  • Hallo,

    Dankeschön für die hilfreichen Antworten!

    Eine Frage hätte ich aber noch, wenn ich nun meine Werte als Referenz übergebe, dann sieht das ja auch ziemlich unschön aus, denn dann müsste ich:

    Bruch bruch_3 = *bruch * *bruch_2;
    

    machen.



  • Dann nimm doch gleich keine Pointer...

    int main() {
        Bruch bruch(3, 5);
        Bruch bruch_2(3, 5);
        Bruch bruch_3 = bruch * bruch_2;
    }
    


  • Mach's doch ohne Pointer:

    const Bruch operator*(Bruch& otherBruch) {
                       int new_zaehler, new_nenner;
                       new_nenner = otherBruch.GetNenner() * this->GetNenner();
                       new_zaehler = otherBruch.GetZaehler() * this->GetZaehler();
                       return Bruch(new_zaehler, new_nenner);
    }
    

    Normalerweise würde ich das aber als freie Funktion machen, da du dann implizite Typumwandlungen nutzen kannst.



  • const Bruch operator*(Bruch& otherBruch) {
                       int new_zaehler, new_nenner;
                       new_nenner = otherBruch.GetNenner() * this->GetNenner();
                       new_zaehler = otherBruch.GetZaehler() * this->GetZaehler();
                       return Bruch(new_zaehler, new_nenner);
    }
    

    Ich würde kein const Bruch zurück geben, ist unnötig. Stattdessen ein const Bruch& als Parameter, da ich im operator* hoffentlich (!) die Parameter nicht verändert werden. Non-const-Referenz verbaut dem Compiler auch Optimierungsmöglichkeiten.



  • Dankeschön!

    Noch ne Frage, wenn ich das so mache, wie Du es vorgeschlagen hast, nämlich:

    return Bruch(new_zaehler, new_nenner);
    

    Wird dann das Objekt erst erstellt wenn ich wieder im main() bin? Oder wird es erstellt, zurückgeschickt und dann aus diesem scope gelöscht?

    Denn ich würde noch Gerne die Memberfunktion Kuerzen() aufrufen, vor dem zurückschicken, aber falls das uneffizient ist, lässt sich auch den Nenner und Zaehler vor der Multiplikation kuerzen.



  • Es wird erstellt, kopiert und anschließend gelöscht. Das kann der Compiler aber optimieren. Wenn du vorher kürzen willst, dann tu es doch:

    Bruch newbruch(new_zaehler, new_nenner);
       newbruch.kuerzen();
       return newbruch;
    }
    


  • Danke für die Hilfe!

    Noch eine letzte Frage habe ich.
    Weiter oben hast du angedeutet, dass man so etwas erstellen könnte:

    bruch * bruch_1 * bruch_2 .. etc.

    Ganz allgemein zum Operator Overloading, wie würde man das denn anstellen?

    MfG



  • So, wie es ist.
    bruch1 * bruch2 * bruch3 ist das gleiche wie
    (bruch1 * bruch2) * bruch3



  • OperatorOverloading schrieb:

    Danke für die Hilfe!

    Noch eine letzte Frage habe ich.
    Weiter oben hast du angedeutet, dass man so etwas erstellen könnte:

    bruch * bruch_1 * bruch_2 .. etc.

    Ganz allgemein zum Operator Overloading, wie würde man das denn anstellen?

    Probiers doch aus...
    Mit folgendem Operator geht das von selbst:

    Bruch operator*(const Bruch& left, const Bruch& right)
    

    Ist jetzt als freie Funktion, das geht auch. Ist hier angebracht, da beide Operanden gleichberechtigt sind.



  • Du brauchst den Operator also nur einmal -- ganz normal -- zu überladen.

    Du kannst dir das so vorstellen, dass bei

    bruch1 * bruch2 * bruch3
    

    zuerst

    bruch1 * bruch2
    

    ausgewertet wird und ein temporäres Objekt vom Typ Bruch erzeugt. Nennen wir es vorläufig zwischenresultat , um das Verständnis zu erleichtern. Der gesamte Ausdruck wird dann zu:

    zwischenresultat * bruch3
    

    So kann der operator* erneut auf die beiden Objekte angewandt werden, und man erhält das endgültige Resultat.



  • Aha, ich verstehe!

    Danke vielmal 😃


Anmelden zum Antworten