K
Wie schon gesagt wurde, würde ich hier aber nicht double verwenden, um Zähler und Nenner zu speichern.
Da es sich bei einem Bruch um einen Typen handelt, den man im Idealfall ähnlich wie double auch verwenden kann, solltest Du vielleicht double durch ein anderes Design mehr approximieren. Beispielsweise kann man Ganzzahlen zu double konvertieren. Das macht bei Brüchen als Zieltyp auch Sinn. In C++ bekommst Du dieses Verhalten durch einen "konvertierenden Konstruktor", einer, der nicht explicit ist und mit einem Parameter aufrufbar ist.
class bruch
{
long zaehler;
long nenner; // immer größer oder gleich 1 halten!
void kuerzen();
public:
bruch(long z=0) // nicht explicit, mit einem Parameter aufrufbar
: zaehler(z), nenner(1)
{ }
bruch(long z, long n)
: zaehler(z), nenner(n)
{ kuerzen(); }
...
};
So kannst du dann z.B. folgendes aufschreiben:
void foo(bruch z);
int main() {
bruch n; // hat jetzt den Wert 0
bruch b (3,2); // drei Halbe
bruch q = 42; // klappt auch, bruch(42)
foo(9); // geht auch, hier entsteht zwischendurch
// ein temporäres Objekt per bruch(9)
}
Jetzt kommen wir zu den Operatoren. Beispielhaft für die Addition:
class bruch
{
long zaehler;
long nenner;
void kuerzen();
public:
...
bruch& operator+=(bruch const& rhs)
{
zaehler = zaehler*rhs.nenner + rhs.zaehler*nenner;
nenner *= rhs.nenner;
kuerzen();
return *this;
}
};
bruch operator+(bruch const& b1, bruch const& b2)
{
bruch ergebnis = b1;
ergebnis += b2;
return ergebnis;
}
Das schöne dabei ist jetzt wieder, dass dank des Konstruktors, man auch Ganzzahlen zu Brüchen addieren kann:
int main() {
bruch b (2,3);
bruch x = b+b; // bruch + bruch
bruch y = 9+b; // int + bruch
bruch z = b+9; // bruch + int
}
Ich möchte darauf hinweisen, dass operator+ absichtlich hier als freie Funktion implementiert ist, da, wenn man dies als "Methode" -- nicht-statische Elementfunktion -- machen würde, 9+b nicht mehr funktionieren würde, weil die linke Seite kein Bruch mehr ist sondern ein int. Für eine solche Methode müsste aber die linke Seite schon ein Bruch sein.
Das Kürzen kannst Du dann ja so implementieren:
void bruch::kuerzen()
{
assert(nenner != 0);
if (nenner<0) {
nenner = -nenner;
zaehler = -zaehler;
}
const long t = ggt(atd::abs(zaehler),nenner);
zaehler /= t;
nenner /= t;
}
und auch am Ende des Konstruktors mal audrufen.
Den GGT berechnest Du dann über den Euklidischen Algorithmus.
Um die Sache rund zu machen, fehlt natürlich noch die Subtraktion, die Multiplikation, die Division und auch die Vergleichsoperatoren, damit Du Deine Klasse genauso verwenden kannst wie double und int auch. Ist das nicht toll? Ich finde schon.
P.S.: Ich sehe gerade, dass du <assert.h> einbindest. Da würde ich lieber <cassert> schreiben; denn das ist die C++ Variante davon. Funktioniert aber genauso.
Cheers!
k.k.