C++ Anfänger und Brüche :)



  • Hi ich bin im Gebiet was C++ angeht vollkommender Neuling und hab wirklich nicht großartig viel Ahnung und versuch mir sie gerade zu verschafen 🙂
    Nun ich wollte ein kleines Programm schreiben mit dem ich Brüche eingeben/ausgeben/addieren/subtrahieren kann

    Nach kurzem ungebremsten Coden war ich dann auch schon fertig, dummerweise funktioniert es nicht wirklich und ich weiß einfach nicht wo der Schuh drückt

    Ach und ignoriert es einfach mal das ich keine Header verwende, damit wollte ich später anfangen

    Also würd mich freuen wenn hier jemand kurz einen Blick drüber wirft
    Vielen Dank

    #include <iostream>
    #include <assert.h>
    using namespace std;
    
    class Bruch {
    private :
    	double zaehler;
    	double nenner;
    
    public :
    
    	// Standart Konstruktor
    	Bruch() {
    		zaehler = 0;
    		nenner = 0;
    	}
    
    	// Konstruktor der keine Division durch 0 Zulässt
    	Bruch(double cZaehler, double cNenner) {
    		assert(nenner != 0);
    		zaehler = cZaehler;
    		nenner = cNenner;
    	}
    
    	// Berechnet Addition zwischen zwei Brüchen
    	void addiere(Bruch b) {
    		zaehler = nenner*b.zaehler + b.nenner*zaehler;
    		nenner = nenner*b.nenner;
    	}
    
    	// Berechnet Suptraktion zwischen zwei Brüchen
    	void subtrahiere(Bruch b) {
    		zaehler = (nenner * b.zaehler) - (b.nenner * zaehler);
    		nenner = nenner * b.nenner;
    	}
    
    	// Gibt den aktuellen Bruch aus
    	void getBruch(){
    		cout << "Aktueller Bruch ist : " << zaehler << "/" << nenner << endl;
    	}
    
    	// Setzt Bruch durch Konstruktor
    	void setBruch(double zaehler, double nenner){
    		Bruch(zaehler, nenner);
    	}
    
    };
    
    	int main() {
    
    		int eingabe = 0;
    		double zaehler = 0;
    		double nenner = 1;
    
    			cout << "Geben sie zuerst den Zaehler ein und dann den Nenner" << endl;
    			cin >> zaehler;
    			cin >> nenner;
    			Bruch b1;
    			b1.setBruch(zaehler, nenner);
    			cout << " Bruch wurde gesetzt" << endl;
    			b1.getBruch();
    
    		cout << " Geben sie 1 für Addition und 2 für Subtraktion ein" << endl;
    			cin >> eingabe;
    
    		switch(eingabe) {
    			case 1:
    				cout << "Geben sie den Zaehler und dann den Nenner ein" << endl;
    				cin >> zaehler;
    				cin >> nenner;
    				Bruch b2;
    				b2.setBruch(zaehler, nenner);
    				b1.addiere(b2);
    				b1.getBruch();
    			case 2:
    				cout << "Geben sie den Zaehler und dann den Nenner ein" << endl;
    				cin >> zaehler;
    				cin >> nenner;
    				Bruch b2;
    				b2.setBruch(zaehler, nenner);
    				b1.subtrahiere(b2);
    				b1.getBruch();
    			default: 
    				cout << " Es können nur 1 oder 2 eingegeben werden" << endl;
    		}
    
    		return 0;
    	}
    


  • Lenard schrieb:

    // Setzt Bruch durch Konstruktor
    	void setBruch(double zaehler, double nenner){
    		Bruch(zaehler, nenner);
    	}
    

    Das tut nicht, was du glaubst. Hier erzeugst du nur ein weiteres, temporäres Bruch-Objekt. Die Member von aufgerufenen Bruch-Objekts, die du hier eigentlich ändern willst, bleiben unangetastet.

    Und für's nächste Mal: Siehe meine Signatur.



  • Heyy! Erstmal ein großes Lob, du hast nicht nur die cpp-Tags auf Anhieb gefunden, sondern den Code auch mehr oder weniger brauchbar eingerückt. Aber:

    Lenard schrieb:

    Nach kurzem ungebremsten Coden war ich dann auch schon fertig, dummerweise funktioniert es nicht wirklich und ich weiß einfach nicht wo der Schuh drückt

    Ernsthaft? Das ist deine Fehlerbeschreibung?
    - Erwartetes Verhalten
    - Auftretendes Verhalten



  • cooky451 schrieb:

    Heyy! Erstmal ein großes Lob, du hast nicht nur die cpp-Tags auf Anhieb gefunden, sondern den Code auch mehr oder weniger brauchbar eingerückt. Aber:

    Lenard schrieb:

    Nach kurzem ungebremsten Coden war ich dann auch schon fertig, dummerweise funktioniert es nicht wirklich und ich weiß einfach nicht wo der Schuh drückt

    Ernsthaft? Das ist deine Fehlerbeschreibung?
    - Erwartetes Verhalten
    - Auftretendes Verhalten

    Ah sorry, nun ich hab es in folgendes gewandelt

    void setBruch(double cZaehler, double cNenner){
    		zaehler = cZaehler;
    		nenner = cNenner;
    	}
    

    dennoch funktioniert immernoch nichts ach und fehlermeldung

    1>------ Erstellen gestartet: Projekt: Aufgaben 3 2, Konfiguration: Debug Win32 ------
    1>  Bruch.cpp
    1>c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(78): error C2360: Initialisierung von 'b2' durch 'case'-Marke übersprungen
    1>          c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(74): Siehe Deklaration von 'b2'
    1>c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(82): error C2086: 'Bruch b2': Neudefinition
    1>          c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(74): Siehe Deklaration von 'b2'
    1>c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(86): error C2361: Initialisierung von 'b2' durch 'default'-Marke übersprungen
    1>          c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(82): Siehe Deklaration von 'b2'
    1>c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(86): error C2361: Initialisierung von 'b2' durch 'default'-Marke übersprungen
    1>          c:\users\computer\documents\visual studio 2010\projects\aufgaben 3 2\aufgaben 3 2\bruch.cpp(74): Siehe Deklaration von 'b2'
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========
    


  • Du darfst keine Variablendefinitionen im gleichen Scope überspringen. Du kannst aber frei einen neuen Scope einführen.

    switch (x)
    {
    case 1:
    { // hier
      int i;
    }
    case 2:
    {
      int a;
    }
    }
    


  • cooky451 schrieb:

    Du darfst keine Variablendefinitionen im gleichen Scope überspringen. Du kannst aber frei einen neuen Scope einführen.

    switch (x)
    {
    case 1:
    { // hier
      int i;
    }
    case 2:
    {
      int a;
    }
    }
    

    Ah Danke das wusste ich nicht nun funktioniert es wie gewünscht, das Motiviert 🙂 vielen dank !



  • Schau doch mal, was genau du hier überprüfst:

    Bruch(double cZaehler, double cNenner) {
            assert(nenner != 0);
            zaehler = cZaehler;
            nenner = cNenner;
        }
    

    Ich bin mir auch nicht sicher, ob es eine gute Idee ist, den Nenner im Standardkonstruktor auf Null zu setzen...



  • Ich hätte zwei kleine Anmerkungen noch :). Ich würde an Deiner Stelle anstatt «doubles» die Ganzzahlren nehmen. Und das Problem mit der 0 würde ich anders angehen. Du teilst ja erst mal nichts, also kannst Du die Null im Nenner stehen lassen. Es ist ein „syntaktisch“ richtiger Bruch, den man ausgeben kann. Es ist ist nur ungültig den auszuwerten. Die Überprüfung auf Null würde ich erst bei in der «Teilen»-Methode als exception/Fehlermeldung machen.
    Dein Code hat ein Problem, dass die «assert»—Anweisung in der Realesebuild weg ist und der Benutzer ( auch Du ) kann dann fröhlich die Nullen eingeben. Weiterhin muss Du in der Methode «setBruch» auch den Nenner auf die Null überwachen.
    Alternativ kannst Du natürlich in dem Konstruktor und «SetBruch» die exception werfen. Aber assert ist einfach ungeeignet die I/O - Fehler zu registrieren.



  • 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.


Anmelden zum Antworten