Skip to content
Snippets Groups Projects
Commit 1c672199 authored by Guttmann, Michael's avatar Guttmann, Michael
Browse files

init exercise

parent 110eb931
No related branches found
No related tags found
No related merge requests found
# Übungsbeispiel: Bruch
Ziel dieses Übungsbeispiels ist es, Operator-Overloading und Exception-Handling in einem kurzen Programm zu implementieren.
### Aber um was geht es denn?
Es soll eine Klasse `Fraction`, die einen Bruch - also eine rationale Zahl - darstellt, erstellt werden. Nach fertiger Implementierung soll mit dieser Klasse wie mit gewöhnlichen `int` oder `double` mit den Operatoren `+`, `*`, `==` ... gerechnet werden können.
Beim Rechnen mit Brüchen ist außerdem zu beachten, dass der Nenner nie `0` werden darf. Insbesondere beim Erstellen eines neues Bruchs, aber auch beispielsweise beim Dividieren könnte es hier zu einem Problem kommen. Dafür soll eine eigene Exception `NullDivisionException` implementiert werden.
## Klasse Fraction
Die Klasse `Fraction` soll genau einen Bruch darstellen. Dafür werden zwei Attribute benötigt, nämlich ein Zähler (`nominator_`) und ein Nenner (`denominator_`).
Ein Bruch soll intern nach jeder Operation immer in gekürzter Form gespeichert sein und der Nenner soll immer positiv sein. Beispielsweise `5/-15` soll als `-1/3` gespeichert werden.
### Konstruktoren
Ein Bruch soll auf drei Arten erstellt werden können:
| Parameter | Beschreibung |
| --------- | ------------ |
| keiner | Der Bruch soll den Wert `0` besitzen. |
| `int` | Der Bruch soll den Wert der Ganzzahl besitzen. |
| `int`, `int` | Erster Paramter ist der Zähler, zweiter der Nenner. |
Im letzten Fall ist außerdem zu überprüfen, dass es zu keiner Division durch `0` kommt, ansonsten soll eine `NullDivisionException` geworfen werden. Außerdem ist zu beachten, dass der Bruch in oben beschriebenen Form gespeichert werden soll.
*Tipp: Es ist nicht notwendig drei unterschiedliche Konstruktoren zu erstellen. Wenn man es geschickt löst, kommt man mit einem aus.*
Copy-Konstruktor bzw. Copy-Assignment-Operator sollen als Übung explizit implementiert werden. Wichtig beim Copy-Assignment-Operator ist es, dass auch folgendes möglich sein soll: `b1 = b2 = b3`.
*Tipp: Überlege ob es hier auch zu einer Division durch `0` kommen kann bzw. ob der Bruch gekürzt werden muss.*
### Methoden
- `getNominator()`
- Getter für den Zähler
- `getDenominator()`
- Getter für den Nenner
- `value()`
- gibt den Wert des Bruchs als `double` zurück
- `reduce()`
- private Methode, um den Bruch zu kürzen und den Nenner positiv zu machen
*Tipp: Euklidischer Algorithmus. Ansonsten kann man auch alle möglichen Teiler durchprobieren, ist zwar nicht besonders effizient, aber gut genug für den Anfang.*
### Ausgabeoperator
Folgender Ausgabeoperator soll implementiert werden, `b1` ist ein Objekt der Klasse `Fraction`.
- `std::cout << b1`
- `b1` soll in der Form: `<nominator_> / <denominator_>` ausgegeben werden
### Arithmetische Operatoren
Folgende Operationen sollten auf jeden Fall implementiert werden, wobei der Copy-Assignment-Operator bereits implementiert sein sollte. `b1`, `b2`, `b3` sind Brüche, `a` ist ein `int`.
*Tipp: Manche Operatoren sind einfacher als Methode zu implementieren, andere wiederum müssen sogar unbedingt als Funktion implementiert werden.*
- `b1 = b2 + b3`
- `b2` und `b3` werden zusammenaddiert, ändern sich aber nicht
- `b1 = b2 + a`
- `b2` und `a` werden zusammenaddiert, ändern sich aber nicht (`a` ist ein `int`!)
- `b1 = a + b3`
- `a` und `b3` werden zusammenaddiert, ändern sich aber nicht (`a` ist ein `int`!)
- `b1 = b2 += b3`
- `b3` wird direkt auf `b2` addiert
- das Ergebnis kann an `b1` zugewiesen werden
- `b1 = b2++`
- post-increment
- in `b1` wird der alte Wert von `b2` geschrieben
- anschließend wird `b2` um den Wert `1` erhöht
- `b1 = ++b2`
- pre-increment
- `b2` wird um den Wert `1` erhöht
- das Ergebnis wird in `b1` geschrieben
- `b1 = -b2`
- `b1` wird das Negative von `b2` zugewiesen
- `b2` bleibt unverändert
- `b1 = ~b2` (bitweiser Operator)
- `b1` wird der Kehrwert von `b2` zugewiesen
- der Nenner soll weiterhin positiv sein
- `NullDivisionException` falls der Nenner `0` ist
- `b2` bleibt unverändert
Alle weiteren arithmetischen Operatoren, wie beispielsweise `-`, `-=`, `--`, `*`, `/`... können analog implementiert werden.
### Logische Operatoren und Vergleichsoperatoren
Folgende Operatoren sollten auf jeden Fall implementiert werden. `b1`, `b2` sind wiederum Brüche.
- `b1`
- ist genau dann `false` sein, wenn der Bruch den Wert `0` besitzt
- **Wichtig! `int a = b1;` darf nicht gültig sein bzw. kompilieren. Daher ist das Schlüsselwort `explicit` zu verwenden.**
- `!b1`
- ist genau dann `true` sein, wenn der Bruch den Wert `0` besitzt
- `b1 == b2`
- ist genau dann `true`, falls `b1` und `b2` denselben Wert besitzen
- `b1 != b2`
- ist genau dann `true`, falls `b1` und `b2` verschiedene Werte besitzen
- `b1 < b2`
- ist genau dann `true`, falls `b1` echt kleiner als `b2` ist
- `b1 <= b2`
- ist genau dann `true`, falls `b1 < b2` oder `b1 == b2`
Alle weiteren Vergleichsoperatoren können analog implementiert werden.
## Klasse NullDivisionException
Diese Klasse stellt eine Exception dar und soll eine Fehlermeldung repräsentieren, wenn in einem Bruch der Nenner `0` werden würde. Daher soll diese Klasse auch von `std::exception` abgeleitet werden. Innerhalb der Klasse soll es genau ein Attribut gegeben, nämlich die Fehlermeldung `message_`, als `std::string`.
### Konstruktor
Weiters soll es genau einen Konstruktor geben, der als einzigen Parameter den Zähler des fehlerhaften Bruchs übergibt bekommt. Anschließend soll in `message_` die Fehlermeldung ```<nominator> / 0 - Dividing through 0 not valid!\n```, wobei `<nominator>` durch den übergebenen Parameter ersetzt werden soll. Der Copy-Konstruktor und der Copy-Assignment-Operator sollen gelöscht werden.
### Methoden
Außerdem soll in der Klasse genau eine Methode implementiert werden, nämlich die `what()`-Methode aus der Basisklasse. Diese Methode liefert als Rückgabewert einen `const char*`, ist selbst `const`, darf selbst keine Exception werfen (`noexcept`) und überschreibt die Methode aus der Basisklasse (`override`). Hier soll genau die `message_` zurückgegeben werden, aber als eben als `const char*`, nicht als `std::string`.
## Testprogramm
Ein kurzes Testprogramm, dass die wesentlichen Funktionalitäten überprüft steht bereits zur Verfügung. Die noch offenen Testcases werden in Kürze noch folgen.
#include <iostream>
#include <cassert>
#include "Fraction.hpp"
#include "NullDivisionException.hpp"
/**
* If you have not implemented all functionality yet, you can simply
* deselect the testcases by setting the corresponding define to 0.
* Then your programm will compile and you can test the other testcases.
*/
#define CONSTRUCTORS 1
#define CONSTRUCTOR_NULL_DIVISION_EXCEPTION 1
#define REDUCE_FRACTION 1
#define COPY_ASSIGNMENT 1
#define OUT_OPERATOR 0 // will follow soon
#define PLUS_OPERATORS 0 // will follow soon
#define PLUS_OPERATORS_WITH_INT 0 // will follow soon
#define INCREMENT_OPERATORS 0 // will follow soon
#define NEGATION_INVERSION 0 // will follow soon
#define BOOL_OPERATOR 0 // will follow soon
#define COMPARE_OPERATORS 0 // will follow soon
void constructors();
void constructorNullDivisionException();
void reduceFraction();
void copyAssignment();
void outOperator();
void plusOperators();
void plusOperatorsWithInt();
void incrementOperators();
void negationInversion();
void boolOperators();
void compareOperators();
//------------------------------------------------------------------------------
int main(void)
{
std::cout << "Starting testcases!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
constructors();
constructorNullDivisionException();
reduceFraction();
copyAssignment();
outOperator();
plusOperators();
plusOperatorsWithInt();
incrementOperators();
negationInversion();
boolOperators();
compareOperators();
std::cout << "Great! Passing all selected testcases!" << std::endl;
return 0;
}
//------------------------------------------------------------------------------
void constructors()
{
#if CONSTRUCTORS == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a;
Fraction b(5);
Fraction c(1, 10);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
"Default constructor not correct!\n");
assert(b.getNominator() == 5 && b.getDenominator() == 1 &&
"Constructor with one paramter not correct!\n");
assert(c.getNominator() == 1 && c.getDenominator() == 10 &&
"Constructor with two parameters not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void constructorNullDivisionException()
{
#if CONSTRUCTOR_NULL_DIVISION_EXCEPTION == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
try
{
Fraction a(2, 0);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(false && "No NullDivisionlException thrown!\n");
}
catch (NullDivisionException& ex)
{
std::cout << "Error: " << ex.what();
assert(std::string(ex.what()) == "2 / 0 - Dividing through 0 not valid!\n"
&& "NullDivisionException not correct!\n");
}
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void reduceFraction()
{
#if REDUCE_FRACTION == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
Fraction b(65, -26);
Fraction c(-123, 126);
Fraction d(-12, -144);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
std::cout << d.getNominator() << " / " << d.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
assert(b.getNominator() == -5 && b.getDenominator() == 2 &&
"Reducing fraction not correct!\n");
assert(c.getNominator() == -41 && c.getDenominator() == 42 &&
"Reducing fraction not correct!\n");
assert(d.getNominator() == 1 && d.getDenominator() == 12 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void copyAssignment()
{
#if COPY_ASSIGNMENT == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(13, -169);
Fraction b(a);
Fraction c(20);
Fraction d;
a = c = d;
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
std::cout << b.getNominator() << " / " << b.getDenominator() << std::endl;
std::cout << c.getNominator() << " / " << c.getDenominator() << std::endl;
std::cout << d.getNominator() << " / " << d.getDenominator() << std::endl;
assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
"Copy-Assignment-Operator not extendable!\n");
assert(b.getNominator() == -1 && b.getDenominator() == 13 &&
"Copy-Constructor not working!\n");
assert(c.getNominator() == 0 && c.getDenominator() == 1 &&
"Copy-Assignment-Operator not working!\n");
assert(a.getNominator() == 0 && a.getDenominator() == 1 &&
"Default Constructor not working!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void outOperator()
{
#if OUT_OPERATOR == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void plusOperators()
{
#if PLUS_OPERATORS == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void plusOperatorsWithInt()
{
#if PLUS_OPERATORS_WITH_INT == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void incrementOperators()
{
#if INCREMENT_OPERATORS == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void negationInversion()
{
#if NEGATION_INVERSION == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void boolOperators()
{
#if BOOL_OPERATOR == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
//------------------------------------------------------------------------------
void compareOperators()
{
#if COMPARE_OPERATORS == 1
std::cout << "Testcase: " << __func__ << std::endl << std::endl;
Fraction a(2, 6);
std::cout << a.getNominator() << " / " << a.getDenominator() << std::endl;
assert(a.getNominator() == 1 && a.getDenominator() == 3 &&
"Reducing fraction not correct!\n");
std::cout << "\nAll good!\n\n";
std::cout << "-----------------------------------------------\n" << std::endl;
#endif
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment