C++-Programmierung/ Brüche/ Die Rechenoperationen

Aus Wikibooks

Wechseln zu: Navigation, Suche


Addition, Subtraktion, Multiplikation und Division, das sind 4 Rechenoperationen. C++ bietet aber die mit der Zuweisung kombinierten Kurzschreibweisen, womit wir insgesamt auf 8 kommen.

Inhaltsverzeichnis

[Bearbeiten] Addition

Um zwei Brüche zu Addieren, müssen die Nenner gleich sein. Wenn wir bereits Brüche haben die sich nicht weiter kürzen lassen, (und dafür sorgt unsere Klasse,) dann erhalten wir wiederum einen unkürzbaren Bruch, wenn wir mit dem kgV erweitern. Das ganze sieht also so aus:

Crystal Clear app terminal.png
Pseudocode
ErgebnisNenner = kgV(Bruch1Nenner, Bruch2Nenner);
ErgebnisZähler = Bruch1Zähler * (ErgebnisNenner / Bruch2Nenner) +
    Bruch2Zähler * (ErgebnisNenner / Bruch1Nenner);

[Bearbeiten] Subtraktion

Für die Subtraktion gelten die gleichen Regeln wie bei der Addition.

Crystal Clear app terminal.png
Pseudocode
ErgebnisNenner = kgV(Bruch1Nenner, Bruch2Nenner);
ErgebnisZähler = Bruch1Zähler * (ErgebnisNenner / Bruch2Nenner) -
    Bruch2Zähler * (ErgebnisNenner / Bruch1Nenner);

[Bearbeiten] Multiplikation

Bei der Multiplikation werden einfach die Zähler und die Nenner multipliziert. Danach muss der Bruch wieder gekürzt werden.

Crystal Clear app terminal.png
Pseudocode
ErgebnisZähler = Bruch1Zähler * Bruch2Zähler;
ErgebnisNenner = Bruch1Nenner * Bruch2Nenner;
kuerzen()

[Bearbeiten] Division

Die Division stimmt mit der Multiplikation fast überein, aber statt die Zähler und die Nenner miteinander zu multiplizieren, werden Sie gegeneinander multipliziert.

Crystal Clear app terminal.png
Pseudocode
ErgebnisZähler = Bruch1Zähler * Bruch2Nenner;
ErgebnisNenner = Bruch1Nenner * Bruch2Zähler;
kuerzen()

[Bearbeiten] Kombination

Da C++ wie schon gesagt neben den normalen Rechenoperatoren noch die mit der Zuweisung kombinierten zu Verfügung stellt, werden wir einen kleinen Trick anwenden, um uns doppelte Arbeit zu ersparen. Wir werden die eigentlichen Rechenoperationen in den Zuweisungskombioperatoren implementieren und dann innerhalb der normalen Rechenoperatoren temporäre Objekte anlegen, für welche wir die Kombinationsoperatoren aufrufen. Das ist ein übliches und vielangewantes Verfahren, welches einige Vorteile zu bieten hat. Sie sparen doppelte Schreibarbeit und müssen sich bei Veränderungen nur um die Kombioperatoren kümmern, da sich die anderen ja genauso verhalten.

Die umgekehrte Variante, also von den Kombioperatoren die normalen aufrufen zu lassen, ist übrigens nicht zu empfehlen, da die Kombinationsoperatoren immer schneller sind, sie benötigen schließlich keine temporären Objekte. Außerdem ist es in vielen Klassen nötig, die normalen Rechenoperatoren außerhalb der Klasse zu deklarieren. Wenn Sie nicht als friend deklariert sind, haben Sie keinen Zugriff auf die privaten Member der Klasse, rufen Sie dagegen die Kombioperatoren auf, brauchen Sie gar keinen Zugriff.

[Bearbeiten] Abschluss

So, nun haben Sie wirklich genug Theorie gehört, es wird Zeit zu zeigen wie das ganze im Quelltext aussieht. Der Code lässt sich zwar noch nicht ausführen, weil der Konstruktor noch nicht definiert ist, aber es lohnt sich trotzdem schon mal einen Blick darauf zu werfen.

Crystal Clear app terminal.png
unsigned int ggT(unsigned int a, unsigned int b){
    if(b == 0)                 // Wenn b gleich 0
        return a;              // ggT gefunden
    else return ggT(b, a % b); // andernfalls weitersuchen
}

unsigned int kgV(unsigned int a, unsigned int b){
    // Das kgV zweier Zahlen, ist ihr Produkt geteielt durch ihren ggT
    return a * b / ggT(a, b);
}

class Bruch{
public:
    Bruch(int zaehler = 0, unsigned int nenner = 1); // noch nicht definiert

    int          zaehler()const {return m_zaehler;}  // Gibt Zähler zurück
    unsigned int nenner()const  {return m_nenner;}   // Gibt Nenner zurück

    Bruch& operator+=(Bruch const &lvalue);
    Bruch& operator-=(Bruch const &lvalue);
    Bruch& operator*=(Bruch const &lvalue);
    Bruch& operator/=(Bruch const &lvalue);

    // Diese Methoden erstellen eine Temporäre Kopie ihres Objekts, führen
    // die Rechenoperation auf ihr aus und geben sie dann zurück
    Bruch operator+(Bruch const &lvalue)const{return Bruch(*this)+=lvalue;}
    Bruch operator-(Bruch const &lvalue)const{return Bruch(*this)-=lvalue;}
    Bruch operator*(Bruch const &lvalue)const{return Bruch(*this)*=lvalue;}
    Bruch operator/(Bruch const &lvalue)const{return Bruch(*this)/=lvalue;}

private:
    void kuerzen();                                  // kürzt weitestmöglich

    int          m_zaehler;
    unsigned int m_nenner;
};


void Bruch::kuerzen(){
    const unsigned int tmp = ggT(m_zaehler, m_nenner);     // ggT in tmp speichern
    m_zaehler /= tmp; // Zähler durch ggT teilen
    m_nenner  /= tmp; // Nenner durch ggT teilen
}

Bruch& Bruch::operator+=(Bruch const &lvalue){
    const unsigned int tmp = kgV(m_nenner, lvalue.m_nenner);
    m_zaehler = m_zaehler * (tmp / m_nenner) + lvalue.m_zaehler * (tmp / lvalue.m_nenner);
    m_nenner  = tmp;
    return *this; // Referenz auf sich selbst zurückgeben
}

Bruch& Bruch::operator-=(Bruch const &lvalue){
    const unsigned int tmp = kgV(m_nenner, lvalue.m_nenner);
    m_zaehler = m_zaehler * (tmp / m_nenner) - lvalue.m_zaehler * (tmp / lvalue.m_nenner);
    m_nenner  = tmp;
    return *this; // Referenz auf sich selbst zurückgeben
}

Bruch& Bruch::operator*=(Bruch const &lvalue){
    m_zaehler *= lvalue.m_zaehler;
    m_nenner  *= lvalue.m_nenner;
    kuerzen();    // Bruch wieder kürzen
    return *this; // Referenz auf sich selbst zurückgeben
}

Bruch& Bruch::operator/=(Bruch const &lvalue){
    m_zaehler *= lvalue.m_nenner;
    m_nenner  *= lvalue.m_zaehler;
    kuerzen();    // Bruch wieder kürzen
    return *this; // Referenz auf sich selbst zurückgeben
}


Persönliche Werkzeuge