Effizients eigenen operator>>



  • Nabend,

    ich habe mir den operator>> überladen um die eingabe eines "Bruches" möglich zu machen.Das bedeutet der User gibt zum Beispiel "2/3" und das Programm kann damit umgehen, gibt der User aber nur "2" ein weiß das Programm es handelt sich um eine Ganzahl. Jetzt meine Frage, ob der operator>> so wie ich ihn euch präsentiere effizient arbeitet, oder ob man da noch was besser machen könnte.

    std::istream &operator>>(std::istream &inside,Fraction &f)
    {
        std::string bruch;
        std::string str_nenner;
        std::string str_zaehler;
        std::stringstream string_to_int;
        int gefunden;
        int neuerzaehler;
        int neuernenner;
    
        inside>>bruch;
        ///Finden des Slashzeichen und Rückgabe desen Position
        gefunden = bruch.find("/");
    
        if(gefunden != std::string::npos)
        {
            ///Aufspaltung in zaehler und nenner
            ///Teilstring von "bruch" bilden(zaehler). Von der Stelle 0 bis zur Stelle "gefunden"
            str_zaehler = bruch.substr(0,gefunden);
            ///Teilstring von Bruch bilden(nenner). Von der Stelle gefunden+1 bis zum Ende des Stringes
            str_nenner = bruch.substr(gefunden+1,bruch.length());
    
           ///Umwandlung von String in Int
           ///Umwandlung für den "zaehler"
           string_to_int<<str_zaehler;
           string_to_int>>neuerzaehler;
    
           f.set_zaehler(neuerzaehler);
    
           ///Umwandlung von String in Int
           ///Umwandlung für den "nenner"
           string_to_int.clear();
           string_to_int<<str_nenner;
           string_to_int>>neuernenner;
    
           f.set_nenner(neuernenner);
    
           f.kuerzen();
    
           return inside;
        }
        else
        {
            string_to_int.clear();
            str_zaehler = bruch;
            string_to_int<<str_zaehler;
            string_to_int>>neuerzaehler;
    
            f.set_zaehler(neuerzaehler);
    
            f.set_nenner(1);
    
            f.kuerzen();
    
            return inside;
    
        }
    }
    


  • Ich denke, es wäre evtl. besser, einen freie Funktion zu bauen, die einen String erwartet, und ein Fraction-Objekt zurückgibt. Das würde flexibler sein, zumal es sicherlich noch ein paar weitere Anwendungsfälle für solch eine Funktion gäbe.
    Dein operator>> müsste dann nur noch diese Funktion aufrufen.



  • Firefighter schrieb:

    ich habe mir den operator>> überladen um die eingabe eines "Bruches" möglich zu machen.Das bedeutet der User gibt zum Beispiel "2/3" und das Programm kann damit umgehen, gibt der User aber nur "2" ein weiß das Programm es handelt sich um eine Ganzahl. Jetzt meine Frage, ob der operator>> so wie ich ihn euch präsentiere effizient arbeitet, oder ob man da noch was besser machen könnte.

    Ja - zumindest ist der Code recht umständlich.
    Du liest zunächst alle Zeichen in einen String (1.Scan) dann durchsuchst Du den String nach '/' (2.Scan) anschließend schreibst Du den String in einen String-Stream (3.Scan) um dann wieder ein 'int' heraus zu lesen (4.Scan).
    Vorschlag: lies doch gleich ein 'int', prüfe ob ein '/' folgt und lese dann den Nenner - auch als 'int' versteht sich.

    std::istream& operator>>( std::istream& inside, Fraction& f )
    {
        int zaehler;
        if( inside >> zaehler )
        {
            std::streambuf* sb = inside.rdbuf();
            if( !inside.eof() && sb->sgetc() == '/' )   // folgt '/' unmittelbar ?
            {
                sb->sbumpc();   // '/' überlesen
                int nenner;
                std::ios_base::fmtflags flgs = inside.flags(); // besser mit boost::io::ios_flags_saver lösen
                if( inside >> std::noskipws >> nenner ) // kein Leerzeichen zwischen '/' und Nenner zulassen
                    f = Fraction( zaehler, nenner ); // kein 'set' kein 'Kürzen' von außerhalb aufrufen
                inside.flags( flgs );   // alten Zustand wiederherstellen
            }
            else
                f = Fraction( zaehler );
        }
        return inside;
    }
    

    Noch ein Hinweis: eine Fraction-Klasse sollte keine set_nenner- oder kuerzen-Methode haben (auch auf die Gefahr hin, dass mir Jester hier widerspricht 😉 ). Jede Methode der Fraction muss dafür sorgen, dass der Bruch am Ende eindeutig ist. Also z.B. folgender Code

    if( Fraction( -2, 4 ) != Fraction( 1, -2 ) )
            cerr << "Fehler in Klasse Fraction" << endl;
    

    darf nie in das 'if' hineinlaufen.

    Intern sollte das daher so aussehen:

    class Fraction
    {
    public:
        explicit Fraction( int zaehler = 0, int nenner = 1 )
            : m_zaehler( zaehler )
            , m_nenner( nenner )
        {
            normalize();
        }
        // ...
    private:
        void normalize() // Kürzen und Nenner positiv machen
        { /*...*/ }
        int m_zaehler, m_nenner;
    };
    

    Tachyon schrieb:

    Ich denke, es wäre evtl. besser, einen freie Funktion zu bauen, die einen String erwartet, und ein Fraction-Objekt zurückgibt. Das würde flexibler sein, zumal es sicherlich noch ein paar weitere Anwendungsfälle für solch eine Funktion gäbe.
    Dein operator>> müsste dann nur noch diese Funktion aufrufen.

    Ich meine Nein - genau das nicht. Die 'universelle' Input-Schnittstelle in C++ ist std::istream nicht std::string.

    Gruß
    Werner


  • Mod

    Werner_logoff schrieb:

    Noch ein Hinweis: eine Fraction-Klasse sollte keine set_nenner- oder kuerzen-Methode haben (auch auf die Gefahr hin, dass mir Jester hier widerspricht 😉 ). Jede Methode der Fraction muss dafür sorgen, dass der Bruch am Ende eindeutig ist. Also z.B. folgender Code

    if( Fraction( -2, 4 ) != Fraction( 1, -2 ) )
            cerr << "Fehler in Klasse Fraction" << endl;
    

    darf nie in das 'if' hineinlaufen.

    Genauer gesagt, man unterscheidet zwischen einem Bruch, der nichts weiter als ein geordnetes Paar von Zahlen ist, und dem Wert eines Bruch = der gebrochenen Zahl, die dieser Bruch darstellt (als solche sind zwei Brüche eben verschieden, wenn sie nicht in Nenner und Zähler übereinstimmen, auch wenn sie die gleiche rationale Zahl darstellen). Beides lässt sich prinzipiell separat in Klassen modellieren, und offensichtlich könnte man eine rationale Zahlenklasse mittels Bruchklasse implementieren. Im Kern geht es also darum, sich festzulegen, was hier programmiert werden soll.


Anmelden zum Antworten