Bruchrechnung



  • #include <iostream>
    
    class Rational
    {
    private:
    	// Daten Elemente
    	int zaehler, nenner;
    public:
    	// Setzen des Zählers und des Nenners
    	void eingabe();
    	void set(int, int);
    
    	// Wert des Zählers bzw Nenners bekommen
    	int  getZ() const;
    	int  getN() const;
    
    	// Kehrwert setzen umtausch des Zählers und des Nenners
    	int kehrwert() const;
    
    	// Rechenfunktionen
    	void add(int, int);  // Additions Bruch
    	void sub(int, int);  // Subtr. Bruch
    	void mult(int, int); // Multi. Bruch
    	void div(int, int);  // Div.   Bruch
    };
    
    int main(int argc, char* argv[])
    {
    
    	return 0;
    }// Main Ende
    

    ICh wollte eine Klasse für die Rechnung mit Brüchen erstellen (aus dem Buch "Der C++ Programmierer" abgeschaut 😃 )
    Meine Frage ist, kann ich die Klasse so lassen oder muss ich sie noch etwas verändern bzw. ist sie komplett falsch?



  • Warum fragst du hier nach der Klasse, wenn du sie aus dem Buch hast? Dort wird sie doch sicher noch weiter vorgestellt und verändert.



  • Ne eben nicht das stand so nicht im Buch ich habe das selbst erstellt nur die Idee dazu hatte ich vom Buch.



  • Die Klasse beschäftigt sich mit Bruchrechnung, da hat eine Funktion eingabe nichts zu suchen.
    Statt set sollte es eine Konstruktor geben.
    Was soll kehrwert machen? Sie ist const, kann also das Objekt nicht verändern und liefert ein int zurück, was nichts mit dem Kehrwert eines Bruchs zutun hat.
    Die Rechenfunktionen sollten Operatoren sein und einen Bruch entgegennehmen.
    Es fehlt ein Zuweisungsoperator.

    Vermutlich bist du noch gar nicht soweit, eine "sinnvolle" Bruchklasse zu schreiben. Das macht nichts. Spiele etwas mit der Klasse so wie du sie hast und arbeite das Buch weiter durch. Dann verstehst du auch, was daran zu verbessern ist.



  • Ich kommentiere mal in den Code rein:

    Aoki_san schrieb:

    #include <iostream>
    
    class Rational // Das hier macht man besser als Klassentemplate (*)
    {
    private:
    	int zaehler, nenner;
    public:
    	void eingabe(); // Was macht diese Funktion? Direkt von der Benutzereingabe lesen? Dann doch lieber den Stream angeben (damit man auch von z.B. Dateien lesen kann!) und das ganze dann als Operator (*) verpacken
    	void set(int, int);
    
    	int  getZ() const;
    	int  getN() const;
    
    	int kehrwert() const; // Wieso gibt diese Funktion etwas zurück? Und wieso ist sie const? Sie verändert den Bruch ja
    
    	void add(int, int); // Diese hier löst man auch alle mit Operatoren (*), aber wieso addierst du zwei ints dazu und nicht eine andere rationale Zahl?
    	void sub(int, int);
    	void mult(int, int);
    	void div(int, int);
    };
    
    int main(int argc, char* argv[])
    {
    
    	return 0;
    }
    

    *: Das kennst du höchstwahrscheinlich noch nicht, wirst du aber noch lernen im Laufe des Buches. Daher ist es auch nicht möglich dass du jetzt schon eine perfekte Klasse schreibst.

    Andere Anregungen:
    - Der Konstruktor fehlt. Ganz wichtig! Damit man sinnvolle Temporaries erstellen kann. Oder dass man nicht immer nach der Definition gleich set aufrufen muss.
    - Funktion zum Kürzen implementieren! Am besten gleich noch den euklidischen Algorithmus verwenden und allgemein als freie Funktion anbieten.
    - Was man einlesen kann sollte man auch ausgeben können.
    - Wenn jemand nur den Zähler oder nur den Nenner verändern möchte, kann er das aktuell komfortabel machen?
    - Eine Funktion, die die Rational in float konvertiert, kann auch wünschenswert sein.



  • Konstruktor mit Default Argumenten:

    Rational(int z=0,int n=1)
    :zaehler(z)
    ,nenner(n)
    {
    }
    

    um dir die ganzen set/get zu sparen kannst du auch einfach Funktionen basteln, die immer gleich heißen - einmal setzen sie den Wert, und einmal geben sie ihn zurück.
    Das ist aber wohl Geschmackssache.

    int zaehler() const;
    void zaehler(int);
    int nenner() const;
    void nenner(int);
    


  • Ich hätte jetzt sowas vorgeschlagen:

    struct Rational
    {
        Rational(int z = 0, int n = 1)
            : zaehler(z), nenner(n)
        { }
    
        int zaehler;
        int nenner;
    };
    
    Rational kehrwert(const Rational &r)
    {
        return Rational(r.nenner, r.zaehler);
    }
    
    Rational add(const Rational &lhs, const Rational &rhs)
    {
        // ...
    }
    
    Rational sub(const Rational &lhs, const Rational &rhs)
    {
        // ...
    }
    
    Rational mul(const Rational &lhs, const Rational &rhs)
    {
        return Rational( lhs.zaehler * rhs.zaehler, lhs.nenner * rhs.nenner );
    }
    
    Rational div(const Rational &lhs, const Rational &rhs)
    {
        // ...
    }
    

    Bemerkungen:
    1. Natürlich wäre es schön hier mit Operatoren-Überladung zu arbeiten aber ich nehme an dass dieses Thema noch nicht behandelt wurde.
    2. Aus meiner Sicht müssen zaehler und nenner nicht unbedingt private sein. Wenn das für dich ein Implementierungsdetail ist dann mach sie halt private und füge entsprechende Methoden hinzu.
    3. Warum freie Funktionen? Ich finds einfach schöner Rational r = sub(r1,r2); zu schreiben als Rational r = r1.sub(r2) . Bei der Funktion kehrwert kann man sich nun streiten ob sie eine freie oder eine Memberfunktion sein soll.

    *Edit
    Deutsche Variablennamen sind IMAO zum kotzen 😉



  • icarus2 schrieb:

    2. Aus meiner Sicht müssen zaehler und nenner nicht unbedingt private sein. Wenn das für dich ein Implementierungsdetail ist dann mach sie halt private und füge entsprechende Methoden hinzu.

    Das ist doch ein Lehrbuchbeispiel für Kapselung. Wie willst du denn sonst die Invariante -- mindestens nenner != 0 -- aufrechterhalten? Ohne das repräsentiert die Klasse keinen Bruch, sondern ein Paar von ganzen Zahlen mit Operationen, die manchmal schief gehen.



  • Bashar schrieb:

    icarus2 schrieb:

    2. Aus meiner Sicht müssen zaehler und nenner nicht unbedingt private sein. Wenn das für dich ein Implementierungsdetail ist dann mach sie halt private und füge entsprechende Methoden hinzu.

    Das ist doch ein Lehrbuchbeispiel für Kapselung. Wie willst du denn sonst die Invariante -- mindestens nenner != 0 -- aufrechterhalten? Ohne das repräsentiert die Klasse keinen Bruch, sondern ein Paar von ganzen Zahlen mit Operationen, die manchmal schief gehen.

    Stimmt, über Invarianten habe ich jetzt nicht nachgedacht. Hast mich überzeugt 😉


Anmelden zum Antworten