C++-Programmierung/ Brüche/ Umwandlung aus anderen Datentypen


Um aus einer Variable eines anderen Datentyps in einen Bruch umzuwandeln, übergibt man einem Konstruktor diese Variable und lässt ihn die Umwandlung durchführen. Sie erinnern sich bestimmt noch daran, dass im ersten Kapitel dieses Abschnittes stand, die Standardparameter des Konstruktors würden später besprochen. Dieser Zeitpunkt ist nun gekommen. Zur Erinnerung, seine Deklaration innerhalb der Klasse lautete:

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

// ...
};

Bruch::Bruch(int zaehler, unsigned int nenner):
    zaehler_(zaehler),
    nenner_(nenner){
    kuerzen();
}

Die Definition steht, wie Sie oben sehen, außerhalb der Klasse. Innerhalb der Initialisierungsliste, welche durch einen Doppelpunkt eingeleitet wird, werden die Membervariablen mit den übergebenen Parametern initialisiert. Innerhalb des Funktionsrumpfes wird die Methode kuerzen() aufgerufen, um den Bruch falls nötig zu kürzen.

Beachten Sie, dass die Standardparameter nur bei der Deklaration, nicht aber bei der Definition einer Funktion angegeben werden. Unser Konstruktor übernimmt 2 int-Werte und beide besitzen einen Standardwert, daher kann er in 3 Formen aufgerufen werden:

Bruch bruch1       // erzeugt (0/1)
Bruch bruch2(5)    // erzeugt (5/1)
Bruch bruch3(3, 4) // erzeugt (3/4)

Die erste Form entspricht einem Defaultkonstruktor und setzt den Wert des Bruches auf (0/1), was dem ganzzahligen Wert 0 entspricht. In der zweiten Form wird 1 int-Wert übergeben, der resultierende Bruch entspricht diesem Wert, da der Nenner dank des Standardparameters auf 1 gesetzt wird. Die dritte Form übernimmt schließlich 2 int-Werte, der erste gibt den Zähler und der zweite den Nenner des Bruches an.

Auch an dieser Stelle soll noch einmal darauf hingewiesen werden, dass der Nenner nicht negativ sein kann, daher ist dieser Parameter auch vom Typ unsigned int welcher nur positive Werte zulässt.

Thema wird später näher erläutert…

Bitte beachten Sie, dass der Nenner eines Bruches normalerweise nicht 0 sein kann. Normalerweise würde man dem in C++ mit einer Ausnahme begegnen. Da wir Ausnahmen aber bisher nicht besprochen haben und es auch noch eine Weile dauern wird, bis wir dieses Thema behandeln, werden wir dem Umstand, dass es möglich ist dem Konstruktor von Bruch eine 0 als Nenner zu übergeben, erst einmal ignorieren. Später kommen wir auf diesen Punkt aber noch einmal zurück.

Gleitkommazahl wird Bruch

Bearbeiten

Wir haben jetzt die Umwandlung von ganzen Zahlen in Brüche besprochen. Als nächstes wollen wir eine Gleitkommazahl in einen Bruch umwandeln. Hierfür benötigen wir einen zweiten Konstruktor, welcher eine Gleitkommazahl übernimmt und sie in einen Bruch umwandelt:

class Bruch{
public:
    Bruch(int zaehler = 0, unsigned int nenner = 1);
    Bruch(double wert);

// ...
};

Bruch::Bruch(double wert):
    zaehler_(static_cast<int>(wert*1000000.0+0.5)),
    nenner_(1000000){
    kuerzen();
}

Es ist kaum möglich, einen double-Wert zuverlässig in einen Bruch umzuwandeln, da die Datentypen, die wir für Zähler (int) und Nenner (unsigned int) verwenden, den Wertebereich unserer Brüche gewaltig einschränken. Dieser Konstruktor ist daher kaum praxistauglich, aber als Beispiel sollte er genügen. Einmal vorausgesetzt, dass ein int 4 Byte groß ist, beachtet er drei Vorkomma- und sechs Nachkommastellen.

Wir setzen den Nenner des Bruches auf 1000000, dann multiplizieren wir den übergebenen Wert mit 1 000 000. Würde man jetzt Zähler durch Nenner teilen, hätte man wieder exakt den Wert, der übergeben wurde. Da der Zähler aber ein int- und kein double-Wert sein muss, müssen wir ihn noch umwandeln. Da eine solche Umwandlung aber alle Nachkommastellen abschneidet, anstatt kaufmännisch korrekt zu runden, addieren wir den double-Wert vorher mit 0.5, was dazu führt, dass die Zahl nach dem Abschneiden der Nachkommastellen kaufmännisch gerundet wurde.

Alles was über 6 Nachkommastellen hinausgeht ist also für uns nicht relevant, da es korrekt gerundet wird. Das Problem besteht darin, dass der Bruch keinen Zähler aufnehmen kann, der größer als der größtmögliche int-Wert (bei 4 Byte 2147483647) ist. Leider lässt sich dieses Problem nicht ohne weiteres lösen, sodass wir uns an dieser Stelle damit zufrieden geben, dass dies nur eine beispielhafte Implementierung für eine Umwandlung einer Gleitkommazahl in einen Bruch und keine perfekte Implementierung ist.

Anschließend wird im Funktionsrumpf wieder einmal die Funktion kuerzen() aufgerufen. Sie können nun also auch schreiben:

Bruch bruch1(0.25)       // erzeugt (1/4)
Bruch bruch2(7.0)        // erzeugt (7/1)
Bruch bruch3(999.999999) // erzeugt (999999999/1000000)

(K)ein Kopierkonstruktor

Bearbeiten

Für unsere Bruch-Klasse werden wir keinen Kopierkonstruktor anlegen, da unser Compiler uns diese Arbeit mit einem zufriedenstellenden Ergebnis abnimmt. Oder anders ausgedrückt, Sie können, ohne eine einzige Zeile Code zur Klasse hinzuzufügen, schreiben:

Bruch bruch1(1, 5);   // erzeugt (1/5)
Bruch bruch2(bruch1); // erzeugt (1/5)