Vorstellung Fraction Klasse



  • hast recht



  • richtig genau das is der Punkt Tachyon. Genau wegen so einem Problem habe ich hier nämlich shconmal im Forum angefragt 🙂


  • Mod

    Die Rückgabe einer Referenz kommt natürlich bei den bereits vorhandenen Operatoren nicht in Frage. Es fehlen auch noch ein paar, einige wurden schon genannt, ich führe nochmal auf
    unär: + -
    binär: == != < > <= >= += -= *= /=
    Eine einfache Konvertierung aus int wäre wünschenswert, es ist relativ natürlich zu schreiben:

    Fraction x = 1;
    

    Sinnvoll würde man hier wohl mit Defaultargumenten arbeiten - insofern halte ich den jetzigen Defaultkonstruktor nicht für günstig, weil er keine Initialisierung durchführt (das kann nat. auch Absicht sein). Für günstiger halte ich

    Fraction(int zaehler=0,int nenner=1);
    

    aber man kann das diskutieren.
    ggT hat nichts mit einer konkreten Instanz von Fraction zu tun, und sollte folglich wenigstens static sein - tatsächlich hat es nicht mal entfernt etwas mit Fraction zu tun - eine freie Funktion ist hier weitaus sinnvoller. Das Problem mit der Interaktion mit float wurde schon angesprochen - das kann man machen, aber da float nicht exakt ist, kann das Ergebnis auch nicht exakt sein, und sollte folglich float sein (analog double, long double). Schließlich erlauben diese Operatoren float-Argumente nur für den rechten Operanden, das ist unintuitiv - die Benutzung von freien Operatoren ist hier absolut angebracht.

    Ob die Streamoperatoren dagegen in der Klasse (+friend) definiert werden, oder erst außerhalb (und möglicherweise nicht friend) ist dagegen absolut und völlig egal. Für den Benutzer spielt es keine Rolle, und auch an der Implementation ändert sich nichts und eine 3. interessierte Partei existiert nicht. Der Kommentar darüber ist allerdings falsch! Was sollen die denn sonst sein, wenn nicht überladene Operatoren?!?



  • Ui, den unären Operator '+' kannte ich gar nicht - sollte der einfach "leer" implementiert werden?



  • ok super danke camper, ich werde deine Hinweise beherzigen und es versuchen soweit umzuschreiben. Ist es denn auch ok wenn ich soviele MEthoden nur für die Operatoren machen? Also ist das bei solchen Klassen normal?

    EDIT: Genau was meinst du mit den unären + -



  • Firefighter schrieb:

    EDIT: Genau was meinst du mit den unären + -

    Für positive und negative Zahlen.

    Fraction a(1,2);
    Fraction b = -a;
    


  • ach du scheiße, daran habe ich ja gar nicht gedacht, stimmt sowas müsste ich noch einfügen. Mal eine Frage wie müsste ich denn += Operator für 2 FRaction objekte bauen?


  • Mod

    Badestrand schrieb:

    Ui, den unären Operator '+' kannte ich gar nicht - sollte der einfach "leer" implementiert werden?

    Er sollte den Wert der Zahl zurückgeben (also keine Referenz, wie man versucht sein könnte). Natürlich macht er nichts interessantes.

    Unäre Operatoren sind solche, die nur einen Operanden haben. Wir möchten z.B. Schreiben:

    Fraction x = ...
    x = -x; // unäres Minus
    x = +x; // unäres Plus - macht im Prinzip gar nichts, existiert der Systematik wegen
    


  • Firefighter schrieb:

    Mal eine Frage wie müsste ich denn += Operator für 2 FRaction objekte bauen?

    So evtl.:

    Fraction& operator+=(const Fraction& right)
    {
        m_numerator = m_numerator * right.m_denominator + right.m_numerator * m_denominator;
        m_denominator = m_denominator * right.m_denominator;
        return *this;
    }
    

    Edit: Das const vor der zurückgegebenen Referenz war mist.


  • Mod

    Mit Templates würde ich noch nicht anfangen - die aufgezeigten Probleme indizieren, dass hier wahrscheinlich noch Lernbedarf besteht. Während man damit einige Probleme elegant lösen kann, ergeben sich auch einige Schwierigkeiten, bei denen man ein gutes Verständnis der "einfachen" Klasse voraussetzen müsste.

    Die Problematik interner Überläufe muss man allerdings in jedem Falle betrachten, solange die Typen für Zähler und Nenner nicht beliebig große Zahlen erfassen können. Dabei gibt es wahrscheinlich keine richtig gute Lösung.
    Schlecht dürfte sein, diese Fälle als Fehler aufzufassen und etwa mit Exceptions zu reagieren. Man kann im Voraus kürzen (dann sollte die Klasse besser rational heißen) aber das ist keine sichere Lösung. Letztlich bleibt dann nur die Möglichkeit, einen genäherten Wert darzustellen - in diesem Moment ist die Klasse dann aber nicht mehr exakt, und die Argumentation hinsichtlich der Interaktion mit float&Co. kehrt sich um. Es kommen auch mehrere Möglichkeiten der Näherung in Frage, boost:rational etwa benutzt den größten darstellbaren Nenner und wählt den Zähler entsprechend. Eine andere Möglichkeit könnte im "übermäßigen" Kürzen bestehen (also Kürzen mit einer Zahl die nicht Teiler von sowohl Zähler als auch Nenner ist). Am Besten - in meinen Augen - dürfte noch sein, die dem exakten Ergebnis nächste darstellbare Zahl zu wählen: das läuft auf eine Kettenbruchentwicklung hinaus (oder vielleicht geht es auch eleganter, meine Mathekenntnisse sind ein bisschen eingerostet), ist also etwas komplexer aber nicht sehr kompliziert.



  • Firefighter schrieb:

    Weiterhin wollte ich fragen was ich alles ändern müsste oder ob es überhaupt Sinn macht wenn ich das ganze als Template machen.

    Macht absolut Sinn. Allerdings sollte für diesen Schritt die jetzige Klasse erstmal einwandfrei funktionieren.

    Badestrand schrieb:

    Ui, den unären Operator '+' kannte ich gar nicht - sollte der einfach "leer" implementiert werden?

    Nein, schliesslich muss der ja etwas zurückgeben. Eine Möglichkeit wäre folgende:

    Fraction operator +() const
    	{
    		return Fraction(+zaehler, nenner);
    	}
    

    @Firefighter
    Noch ein Tipp, binäre Non-Assignment Operatoren solltest du ausserhalb der Klasse, im Namensraum definieren. Notfalls in der Klasse als friend deklarieren, falls du auf non-public Member zugreifen möchtest. Nur mal ein Beispiel, was gegen Member-Funktionen spricht:

    Fraction(1, 3) * 1.5
    1.5 * Fraction(1, 3)
    


  • Ok danke an alle für eure Meinung, besonderen Dank an camper für seine Professionelle Einschätzung und Meinung 🙂 Werde mich wahrscheinlich bei Fragen noch öfters melden.



  • groovemaster schrieb:

    Noch ein Tipp, binäre Non-Assignment Operatoren solltest du ausserhalb der Klasse, im Namensraum definieren. Notfalls in der Klasse als friend deklarieren, falls du auf non-public Member zugreifen möchtest.

    Oder einfach über die Assignment-Operatoren:

    Fraction operator+( const Fraction& frac, const float val )
    {
        Fraction ret( frac );
        ret += val;
        return ret;
    }
    

    So bleibt die Logik in der Klasse; und Redundanz und so.



  • Fraction& Fraction::operator +=(const Fraction& rhs)
    {
        *this = *this + rhs;
        return *this;
    }
    

    Oft sieht man es aber, dass es genau andersrum gemacht wird: Der += operator definiert das Verhalten und der + operator ruft intern den += operator auf.

    Gruß
    Don06



  • Echt? Das hab ich so noch nie gesehen.. Naja, so oder so ist m.M.n. aber Hauptsache, dass "wirklich"-operierende Teil in der Klasse ist, die quasi-Wrapper können draußen bleiben.



  • Don06 schrieb:

    Fraction& Fraction::operator +=(const Fraction& rhs)
    {
        *this = *this + rhs;
        return *this;
    }
    

    Von diesem Code solltest Du absehen: da wird eine überflüssige temporäre Kopie erstellt. Andersherum wird ein Schuh draus.


  • Mod

    nt



  • Konrad Rudolph schrieb:

    Don06 schrieb:

    Fraction& Fraction::operator +=(const Fraction& rhs)
    {
        *this = *this + rhs;
        return *this;
    }
    

    Von diesem Code solltest Du absehen: da wird eine überflüssige temporäre Kopie erstellt. Andersherum wird ein Schuh draus.

    Hab ich nicht darauf hingewiesen, dass es oft andersrum gemacht wird?



  • Don06 schrieb:

    Konrad Rudolph schrieb:

    Don06 schrieb:

    Fraction& Fraction::operator +=(const Fraction& rhs)
    {
        *this = *this + rhs;
        return *this;
    }
    

    Von diesem Code solltest Du absehen: da wird eine überflüssige temporäre Kopie erstellt. Andersherum wird ein Schuh draus.

    Hab ich nicht darauf hingewiesen, dass es oft andersrum gemacht wird?

    Ja, aber wieso überhaupt diesen irreführenden Code zeigen? Badestrand zumindest hast Du auch erfolgreich verwirrt (oder bezog sich das auf was anderes?).



  • Konrad Rudolph schrieb:

    Badestrand zumindest hast Du auch erfolgreich verwirrt (oder bezog sich das auf was anderes?).

    Jau, ich hatte den Satz darunter irgendwie falsch gedeutet 🙂 Don, du hast anderen Code gezeigt und dann was von "andersrum" gesagt, das war einfach zu viel für mich 😃


Anmelden zum Antworten