Klasse mit dem << Operator einlesen?



  • Ich denke, das ist OK so.



  • wxSkip schrieb:

    Ich denke, das ist OK so.

    OK



  • Da ihr mich ja scheinbar überlesen habt, wie wär's denn so?

    #include <iostream>
    #include <exception>
    #include <locale>
    #include <vector>
    #include <algorithm>
    
    class AlterFalsch : public std::exception
    {
        public:
        const char* what() const throw()
        {
            return "Alter in einem ungueltigen Bereich!\n";
        }
    };
    
    class NameFalsch : public std::exception
    {
        public:
        const char* what() const throw()
        {
            return "Ein vollstaendiger Name muss\n-Aus Vor- und Nachnahmen bestehen\n"
                   "-Laenger als 3 Zeichen sein\n"
                   "-Darf keine Sonderzeichen enthalten\n";
        }
    };
    
    bool istUmlaut(char c)
    {
        const char ae = 132, oe = 148, ue = 129,
                   UE = 220, OE = 214, AE = 196;
        std::vector<char> sonderzeichen { ae, oe, ue, AE, OE, UE };
        return std::find(sonderzeichen.begin(), sonderzeichen.end(), c) != sonderzeichen.end();
    }
    
    bool nameInOrdnung(const std::string& name)
    {
        if(name.size() < 3)
            return false;
        unsigned anzahlLeerzeichen = 0; //Ein vollstaendiger Name hat genau 1 Leerzeichen.
        for(auto i : name)
            if(!std::isalpha(i, std::locale("")) && !istUmlaut(i))
            {
                if(i == ' ')
                    ++anzahlLeerzeichen;
                else
                    return false;
            }
        if(anzahlLeerzeichen != 1)
            return false;
        return true;
    }
    
    bool alterInOrdnung(const int& alter)
    {
        return alter <= 150 && alter >= 0;
    }
    
    class Person
    {
        int alter;
        std::string name;
    public:
        Person(const std::string& n, int a) :
        alter(a), name(n) {}
        Person() : alter(0), name("Unbekannt") {}
        std::string Name() const { return name; }
        int Alter() const { return alter; }
        friend std::istream& operator>>(std::istream& in, Person& p) throw(AlterFalsch);
    };
    
    std::ostream& operator<<(std::ostream& out, const Person& p)
    {
        return out << p.Name() << ", " << p.Alter() << " Jahre alt.";
    }
    
    template<typename typ>
    void in(typ& para, std::istream& is = std::cin) //wrapper um cin fuer sichere Eingabe
    {
        while(!(is >> para))
        {
            std::cerr << "Fehler bei der Eingabe!\n";
            is.clear();
            is.ignore(1000, '\n');
        }
    }
    
    std::istream& operator>>(std::istream& is, Person& p) throw(AlterFalsch)
    {
        std::cout << "Name: ";
        std::getline(is, p.name);
        if(!nameInOrdnung(p.Name()))
            throw NameFalsch();
        std::cout << "Alter: ";
        in(p.alter, is);
        if(!alterInOrdnung(p.Alter()))
            throw AlterFalsch();
        return is;
    }
    
    int main()
    {
        Person p;
        try
        {
            std::cin >> p;
            std::cout << p;
        }
        catch(const std::exception& a)
        {
            throw;
        }
        return 0;
    }
    

    😃
    Aber jetzt ernstgemeinte Fragen:

    Ist das gut so? (Ich bring mir das selbst nur zum Spaß bei und andere Menschen sehen selten Code von mir, daher die Frage, kann ja sein, dass ich da was falsch gelernt hab vom Stil her). Und ich hab' festgestellt, dass bei

    std::isalpha(char, std::locale)
    

    mit der Systemlocale auch Umlaute wie ö, ä und ü als false ausgewertet werden und musste mir dann diese blöde istUmlaut schreiben. Warum ist das so, ich dachte die locale wär extra für sowas da?



  • Incocnito schrieb:

    Sollte man aber sparsam mit umgehen, da es ja die ganze Datenkapselung durchlöchert.

    friend stärkt die Kapslung. Es vermeidet, Member oder Memberfunktionen komplett public zu machen, nur damit irgendeine andere Funktion darauf Zugriff hat.



  • Naja, das kann man so aber nicht stehnlassen.
    Besser wäre: Friend schwächt die Datenkapselung nicht so sehr, wie es das Öffentlichmachen von eigentlich privaten Membern macht.
    Aber stärken tut sie sie deswegen nicht. Nur weniger schlimm halt.
    Und zu viel friend weißt sowieso auf ein schlechtes Design hin.
    In der Regel ist es besser, sich bei jedem Verwenden von friend nochmal Gedanken zu machen, ob das überhaupt passieren sollte. In diesem Fall wäre es vllt. besser, der Klasse Person eine Funktion wie toString() const zu verpassen, und der Operator << ruft die dann einfach auf und gibt's aus.
    Aber das spielt hier nicht wirklich ne Rolle. In der Realität brauche ich friend so ziemlich gar nicht. Aber ist ja deine Sache.



  • Incocnito schrieb:

    In diesem Fall wäre es vllt. besser, der Klasse Person eine Funktion wie toString() const zu verpassen, und der Operator << ruft die dann einfach auf und gibt's aus.

    Warum muss ich ein XYZ-Objekt erst in einen String umwandeln, um es ausgeben zu können? Ineffizient.



  • Ja, wär zu viel Aufwand und unnötig, deswegen hab ich ja auch friend verwendet. Ich wollt' nur sagen, dass es (fast?) immer eine Alternative zu friend gibt, die nicht die Datenkapselung schwächt, und die daher zu bevorzugen ist.
    War mir hier nur egal und ich wollte ja nur mal zeigen, dass es auch mit friend ginge.



  • Die Alternative wäre eine Funktion print_to(ostream&) :p
    friend gewinnt dagegen bei mir, da es kürzer ist.



  • Joa, mir ist Kürze nicht so wichtig wie gutes Design.
    Also Maßstabsgerecht natürlich.
    Wenn die einfache Variante deutlich kürzer ist, muss ich mir natürlich überlegen, ob's wert ist. Aber so ein Problem hatte ich bis jetzt noch nie; Wenn ich zwischen kurz oder lang entscheide, geht's da höchstens um ein paar dutzend Zeilen 🤡 Und dann nehm' ich natürlich das hübschere, verzichte auf Sachen wie friend und tippe halt ein paar Sekunden länger.
    Aber wie gesagt, kann jeder machen wie er will.



  • Incocnito schrieb:

    Da ihr mich ja scheinbar überlesen habt, wie wär's denn so?

    #include <iostream>
    #include <exception>
    #include <locale>
    #include <vector>
    #include <algorithm>
    
    class AlterFalsch : public std::exception
    {
        public:
        const char* what() const throw()
        {
            return "Alter in einem ungueltigen Bereich!\n";
        }
    };
    
    class NameFalsch : public std::exception
    {
        public:
        const char* what() const throw()
        {
            return "Ein vollstaendiger Name muss\n-Aus Vor- und Nachnahmen bestehen\n"
                   "-Laenger als 3 Zeichen sein\n"
                   "-Darf keine Sonderzeichen enthalten\n";
        }
    };
    
    bool istUmlaut(char c)
    {
        const char ae = 132, oe = 148, ue = 129,
                   UE = 220, OE = 214, AE = 196;
        std::vector<char> sonderzeichen { ae, oe, ue, AE, OE, UE };
        return std::find(sonderzeichen.begin(), sonderzeichen.end(), c) != sonderzeichen.end();
    }
    
    bool nameInOrdnung(const std::string& name)
    {
        if(name.size() < 3)
            return false;
        unsigned anzahlLeerzeichen = 0; //Ein vollstaendiger Name hat genau 1 Leerzeichen.
        for(auto i : name)
            if(!std::isalpha(i, std::locale("")) && !istUmlaut(i))
            {
                if(i == ' ')
                    ++anzahlLeerzeichen;
                else
                    return false;
            }
        if(anzahlLeerzeichen != 1)
            return false;
        return true;
    }
    
    bool alterInOrdnung(const int& alter)
    {
        return alter <= 150 && alter >= 0;
    }
    
    class Person
    {
        int alter;
        std::string name;
    public:
        Person(const std::string& n, int a) :
        alter(a), name(n) {}
        Person() : alter(0), name("Unbekannt") {}
        std::string Name() const { return name; }
        int Alter() const { return alter; }
        friend std::istream& operator>>(std::istream& in, Person& p) throw(AlterFalsch);
    };
    
    std::ostream& operator<<(std::ostream& out, const Person& p)
    {
        return out << p.Name() << ", " << p.Alter() << " Jahre alt.";
    }
    
    template<typename typ>
    void in(typ& para, std::istream& is = std::cin) //wrapper um cin fuer sichere Eingabe
    {
        while(!(is >> para))
        {
            std::cerr << "Fehler bei der Eingabe!\n";
            is.clear();
            is.ignore(1000, '\n');
        }
    }
    
    std::istream& operator>>(std::istream& is, Person& p) throw(AlterFalsch)
    {
        std::cout << "Name: ";
        std::getline(is, p.name);
        if(!nameInOrdnung(p.Name()))
            throw NameFalsch();
        std::cout << "Alter: ";
        in(p.alter, is);
        if(!alterInOrdnung(p.Alter()))
            throw AlterFalsch();
        return is;
    }
    
    int main()
    {
        Person p;
        try
        {
            std::cin >> p;
            std::cout << p;
        }
        catch(const std::exception& a)
        {
            throw;
        }
        return 0;
    }
    

    😃
    Aber jetzt ernstgemeinte Fragen:

    Ist das gut so? (Ich bring mir das selbst nur zum Spaß bei und andere Menschen sehen selten Code von mir, daher die Frage, kann ja sein, dass ich da was falsch gelernt hab vom Stil her). Und ich hab' festgestellt, dass bei

    std::isalpha(char, std::locale)
    

    mit der Systemlocale auch Umlaute wie ö, ä und ü als false ausgewertet werden und musste mir dann diese blöde istUmlaut schreiben. Warum ist das so, ich dachte die locale wär extra für sowas da?

    Danke für deine Hilfe, nur bin ich noch nicht mit dem Lernen bei template angekommen.



  • Habe einen vector <Person> definiert, wo man ja die Person Strukturen eingeben soll und dann wieder ausgeben.

    vector <Person> ps;
    
    istream& operator>>(istream& is, Person& P)
    {
    	cout << "Person und Alter eingeben" << endl;
    	while (is>>P.name>>P.age)
    	ps.push_back(P.name,P.age);   // Fehler
    	{
    	if(P.age < 0 || P.age > 150)
    		   error ("Alter ist ungueltige!\n");
    	if(!is) return is;
    
    	for(int i = 0;i<P.name.size();++i)
    		switch(P.name[i])
    	    {
    		case ';': case ':': case '"': case '\'': case '[': case ']':
    		case '*': case '&': case '^': case '%': case '$': case '#': 
    		case '@': case '!':
    			error ("Ungueltiger Name!");
    	    }  
    	}
    	return is;
    }
    
    ostream& operator<<(ostream& os, Person& P)
    {
    	return os << P.name2() << " " << P.age2() << endl;
    }
    
    int main()
    try{
    	Person P1("Goofy",63);
    	ostream& os(cout<<P1);
    
    	Person P;
    	istream& is(cin>>P);
    	operator<<(os,P);
    
    	keep_window_open();
    }
    catch (runtime_error e) {	
    	cout << e.what() << '\n';
    	keep_window_open("~");	
    }
    

    Zeile 7 error C2661: 'std::vector<_Ty>::push_back': Keine überladene Funktion akzeptiert 2 Argumente
    Zeile 7 IntelliSense: Keine Instanz von Überladene Funktion "Vector<T>::push_back [mit T=Person]" stimmt mit der Argumentliste überein.



  • Die Fehlermeldung ist doch ziemlich eindeutig... push_back(...) nimmt nur ein Argument, naemlich ein Person Argument in diesem Fall.

    Das muss so aussehen:

    ps.push_back(P);
    


  • Du hast da einen Vektor von Personen. Da kommen auch Personen rein und nicht irgendwelche andere Sachen.

    Also

    ps.push_back(P);
    

    Zu deinem Code:

    Dinge wie die Überprüfung, ob der Name oder das Alter richtig oder falsch sind, sollten besser wie bei mir in eine Funktion ausgelagert werden. Das macht den Code übersichtlicher.

    Und deinen Operator << brauchst du nicht so umständlich als Funktion aufrufen. Die sind extra dafür da, solche Aufrufe zu umgehen und das ganze leichter (lesbarer) zu machen.

    Mach einfach

    int main()
    {
        // Klammer nicht vergessen!
        try
        {
            Person P1("Goofy",63);
            cout << P1;
    
            Person P;
            cin >> P;
            cout << p;
    
            keep_window_open();
        }
        catch (runtime_error e)
        {
            cout << e.what() << '\n';
            keep_window_open("~");
        }
    }
    

    EDIT: Da fehlen die Klammern bei main() in deinem Code.

    EDIT 2: Mir fällt grad auf, dein vector<Person> ps; ist ja global. Globale Variablen sind unschön. Mach den in die main() und füg ihm dort neu eingegebene Personen hinzu. Variablen immer so lokal wie möglich halten.



  • Gemacht der vector ist in der main().

    Jetzt versuche ich die werte im vector auszugeben. Doch es wird keine Ausgabe gemacht obwohl die Funktion ja aufgerufen wird.

    int main()
    try{
    	Person P1("Goofy",63);
    	cout << P1;
    
    	vector <Person> ps;
    
    	Person P;
    	cin >> P; 
    	ps.push_back(P);
        cout << P;
    
    	for(int i = 0;i<ps.size();++i)
    		cout << ps[i] << endl;
    
    	keep_window_open();
    }
    

    Habe jetzt gemerkt das ich die ganze zeit über keine Ausgabe hatte. Nur die erste Person was in der main() definiert war wurde auch ausgeben, alles andere wurde nur eingelesen und nicht ausgeben.


Anmelden zum Antworten