C++-Programmierung/ Brüche/ Die Rechenoperationen
Aus Wikibooks
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:
ErgebnisZähler = Bruch1Zähler * (ErgebnisNenner / Bruch2Nenner) +
Bruch2Zähler * (ErgebnisNenner / Bruch1Nenner);
[Bearbeiten] Subtraktion
Für die Subtraktion gelten die gleichen Regeln wie bei der Addition.
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.
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.
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.
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
}