zwei Brüche Addieren!!


  • Gesperrt

    @DocShoe sagte in zwei Brüche Addieren!!:

    Ich will dir ja nicht zu nahe treten, aber das geht in C++ anders.

    Eh, wie denn?



  • @EinNutzer0 sagte in zwei Brüche Addieren!!:

    @DocShoe sagte in zwei Brüche Addieren!!:

    Ich will dir ja nicht zu nahe treten, aber das geht in C++ anders.

    Eh, wie denn?

    Z.B.

    Bruch (int z, int n) : zaehler(z), nenner (n) {};
    

    Den Rest dann gerne, wenn nicht mehr die Gefahr besteht, dass es dennoch genutzt wird.

    Und es wird der operator+ gefordert und nicht operator+=, d.h. return *this ist da schon falsch.


  • Mod

    @EinNutzer0 sagte in zwei Brüche Addieren!!:

    @DocShoe sagte in zwei Brüche Addieren!!:

    Ich will dir ja nicht zu nahe treten, aber das geht in C++ anders.

    Eh, wie denn?

    Du sagst doch selbst, dass dein printAll katastrophal ist. Gänzlich diese Funktion (und allen zugehörigen Hilfscode wie Zeile 48) wegzulassen würde schon sehr helfen.


  • Gesperrt

    @john-0 sagte in zwei Brüche Addieren!!:

    Und es wird der operator+ gefordert und nicht operator+=, d.h. return *this ist da schon falsch.

    Das steht dort nur, weil ich die Implementierung offen lassen wollte...

    Zu printAll ist mir aber nix besseres eingefallen.



  • Generell ist zur Aufgabe noch anzumerken, dass add und operator+ natürlich const sein sollten - und statt print wäre es sinnvoller, den operator<< zu implementieren.



  • Also ich sehe den Anwendungsfall für printAll() nicht. Entweder liegen die einzelnen Brüche als konkrete Variablen vor, dann kann man sie auch separat ausgeben, oder sie liegen in einem Container vor (std::vector, std::array, oder von mir aus auch Bruch[]), dann kann man per std::begin und std::end drüber iterieren und ausgeben.
    Und wenn man eine bequeme Komfortfunktion haben möchte kann man das über variadic templates machen, aber das ist nur Spielerei:

    #include <iostream>
    #include <utility>
    
    struct Bruch
    {
       int N = 1;
       int Z = 1;
    
       Bruch() = default;
       Bruch( int n, int z ) : N( n ), Z( z ) {}
    };
    
    void print( Bruch const& b )
    {
       std::cout << b.N << '/' << b.Z << '\n';
    }
    
    template<typename ...T>
    void print( Bruch const& b, T&&... params )
    {
       print( b );
       print( std::forward<T>( params )... );
    }
    
    int main()
    {
       Bruch b1 { 10, 1 };
       Bruch b2 { 20, 1 };
       print( b1, b2 );
    }
    


  • @wob sagte in zwei Brüche Addieren!!:

    dass add ... natürlich const sein sollten

    Wobei man sich noch überlegen sollte, wo das const hin soll (an den Anfang oder ans Ende ).
    Ich würde jetzt raten, dass eine Memberfunktion "add" das Objekt ändert.
    Wie auch immer, in der Form "Bruch Bruch::add(Bruch op)" jedenfalls eine Katastrophe.


  • Gesperrt

    @DocShoe sagte in zwei Brüche Addieren!!:

    über variadic templates

    Das sieht besser aus, Danke.



  • @Jockelx sagte in zwei Brüche Addieren!!:

    Ich würde jetzt raten, dass eine Memberfunktion "add" das Objekt ändert.

    Hm, ich hatte gedacht, dass die das nicht tun soll, weil sie Bruch returnt, dachte also an operator+ in "unfancy".



  • @wob Klar, kann auch sein. Wie gesagt, bei der Signatur kann man ja nur raten.
    Letztendlich ja auch egal, wichtig ist ja erstmal, dass der TE jetzt weiß, dass die Signatur so wie sie ist, Mist ist.



  • Leute vielen Dank für die Antworten, @DocShoe es sind Keine Hausaufgaben sondern eine Aufgabe von einer alte Klausur, und glaube nicht das das ganze für 2 Punkten ist🤔 . und da ich keine Lösung dafür habe , wollte hier mal fragen, implementieren muss man hier keine Methode...
    @Jockelx Danke trotzdem , musst du aber nicht so werden, da du auf die Fragen hier im Forum nicht antworten musst, lass es, wenn der TE nicht so schreibt wie du gut findest😉



  • @wob sagte in zwei Brüche Addieren!!:

    und statt print wäre es sinnvoller, den operator<< zu implementieren.

    ich persönlich bevorzuge ein toString(). Da kann man dann nutzen wie man lustig ist und der Overhead ist meistens egal.



  • @Tyrdal sagte in zwei Brüche Addieren!!:

    ich persönlich bevorzuge ein toString(). Da kann man dann nutzen wie man lustig ist und der Overhead ist meistens egal.

    Dann heißt es bei der einen Klasse toString(), bei der nächsten to_string() und bei der dritten to_chars() und bei der vierten noch wieder anders. Wenn du generisch beliebige Objekte ausgeben willst, ist das doof. Aber du kannst einfach schreiben:

    std::ostream &operator<<(ostream &os, const DeinT o) { os << o.toString(); return os; }

    Ich würde den Operator immer klein halten - du kannst auch eine Fkt void print_to_stream(ostream&) in deiner Klasse implementieren und die im operator<< aufrufen - damit gehen auch virtuelle Funktionen für <<.



  • Ich hatte ja letzte Woche schon eine Musterlösung mit den exakt gleichen Methodendeklarationen geschrieben. Ich würde das so nicht schreiben, aber egal war nicht die Aufgabenstellung. Man kann die drei Methoden effektiv auf eine zurückführen, so dass der Code nur einmal wirklich vorhanden ist. Ich weiß jetzt nicht, ob das im Sinne des Aufgabensteller war, aber es bietet sich bei der so formulierten Aufgabe halt einfach an.

    #include <cmath>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <stdexcept>
    
    class Bruch {
    	int zaehler;
    	int nenner;
    public:
    	Bruch (const int z, const int n) : zaehler(z), nenner (n) {
    		if (0 == nenner) {
    			throw std::logic_error ("division by zero not defined");
    		}
    	};
    
    	Bruch add (Bruch op) {
    		return Bruch::add_2(*this, op);
    	};
    	static Bruch add_2 (Bruch op1, Bruch op2) {
    		int n = op1.nenner * op2.nenner;
    		int z = (op1.zaehler * op2.nenner) + (op2.zaehler * op1.nenner);
    		int m = sqrt (abs(std::min (n, z))) + 1;
    
    		for (int i = 2; i < m; ++i) {
    			while ((0 == z % i) && (0 == n % i)) {
    				z /= i;
    				n /= i;
    			}
    		}
    
    		Bruch r (z, n);
    		return r;
    	}
    	Bruch operator+ (Bruch op) {
    		return Bruch::add_2(*this, op);
    	};
    
    	void print() {
    		std::cout << zaehler << '/' << nenner << std::endl;
    	}
    };
    
    int main () {
    	try {
    		Bruch a (4, 5);
    		Bruch b (3, 10);
    		//Bruch x (1, 0);
    		a.print();
    		b.print();
    
    		Bruch c = a.add(b);
    		c.print();
    
    		Bruch d = Bruch::add_2 (a, b);
    		d.print();
    
    		Bruch e = a+b;
    		e.print();
    	}
    	catch (std::exception& e) {
    		std::cerr << "exception!!!" << "\n" << e.what() << std::endl;
    
    		return EXIT_FAILURE;
    	}
    
    	return EXIT_SUCCESS;
    }
    

    Nachtrag: Für die Wurzel aus dem Zähler den Betrag ergänzt.


  • Mod

    @john-0 sagte in zwei Brüche Addieren!!:

    Ich hatte ja letzte Woche schon eine Musterlösung mit den exakt gleichen Methodendeklarationen geschrieben. Ich würde das so nicht schreiben, aber egal war nicht die Aufgabenstellung. Man kann die drei Methoden effektiv auf eine zurückführen, so dass der Code nur einmal wirklich vorhanden ist. Ich weiß jetzt nicht, ob das im Sinne des Aufgabensteller war, aber es bietet sich bei der so formulierten Aufgabe halt einfach an.

    Der Sinn der Aufgabenstellung war überhaupt gar nicht, eine Implementierung der Funktionen vorzulegen. Es ging darum, Funktionssignaturen in einen passenden Aufruf zu überführen. Eine durchaus wichtige Grundfähigkeit bei der Nutzung von fremden Libraries. Also die Zeilen 46-59.

    Das sind dann auch (plus Leerzeilen und der Ausgabe von a, b) exakt die 5+3 Zeilen, zu denen ich weiter oben einen Tipp gegeben habe.



  • @wob sagte in zwei Brüche Addieren!!:

    @Tyrdal sagte in zwei Brüche Addieren!!:

    ich persönlich bevorzuge ein toString(). Da kann man dann nutzen wie man lustig ist und der Overhead ist meistens egal.

    Dann heißt es bei der einen Klasse toString(), bei der nächsten to_string() und bei der dritten to_chars() und bei der vierten noch wieder anders. Wenn du generisch beliebige Objekte ausgeben willst, ist das doof. Aber du kannst einfach schreiben:

    std::ostream &operator<<(ostream &os, const DeinT o) { os << o.toString(); return os; }

    Ich würde den Operator immer klein halten - du kannst auch eine Fkt void print_to_stream(ostream&) in deiner Klasse implementieren und die im operator<< aufrufen - damit gehen auch virtuelle Funktionen für <<.

    Tja nu, ich mag die streams in c++ halt so ganz und gar nicht. So hat jeder seine Vorlieben und Abneigungen.



  • @SeppJ sagte in zwei Brüche Addieren!!:

    Der Sinn der Aufgabenstellung war überhaupt gar nicht, eine Implementierung der Funktionen vorzulegen. Es ging darum, Funktionssignaturen in einen passenden Aufruf zu überführen. Eine durchaus wichtige Grundfähigkeit bei der Nutzung von fremden Libraries. Also die Zeilen 46-59.

    Letzte Woche fehlte der Hinweis auf eine Klasur ja noch. Wenn man das als Übungsaufgabe hat, erwarte ich halt, dass da ein lauffähiges Programm dabei heraus kommt, und man direkt sehen kann, ob die Lösung stimmt. Dazu kann man entweder direkt eine Implementation mitliefern, oder der Student hat das zu liefern.


Anmelden zum Antworten