Pointer auf Arrays aus Structs



  • WOW, das geht ja richtig schnell mit euren Antworten 🙂

    Hab jetzt das ganze völlig neu überdacht und als verkettete Liste implementiert weil mir diese so gut gefallen 😉

    Hier der Code bis jetzt (funktioniert schon gut)

    /* 
     * File:   bank.cpp
     * Author: Nils Reich
     *
     * Created on 29. Januar 2009, 12:43
     */
    
    #include <iostream>
    #include <stdlib.h>
    #include <string>
    #include <fstream>
    
    using namespace std;
    
    /** Klasse mit Definitionen fuer eine einzelne Person
     *  Hier wurden alle oeffentlichen Members der Struktur, die extern
     *  verwendet werdeb durch get- und set-Funktionen ersetzt
     *  Es ist so kein direkter Zugriff auf die Daten mehr moeglich
     */
    class Person {
    private:
        int id;
        string name;
        string vorname;
        int alter;
        string strasse;
        int nr;
        string wohnort;
        int postleitzahl;
        double kontostand;
        Person *next;
    public:
        Person(int id, string name, string vorname, int alter, string strasse, int nr, string wohnort, int postleitzahl, double kontostand);
        void setNext(Person *next);
        Person *getNext();
        void print();
        int getID();
        ~Person();
        void write(ofstream &out);
    };
    
    /* Constructor */
    Person::Person(int pid, string pname, string pvorname, int palter, string pstrasse, int pnr, string pwohnort, int ppostleitzahl, double pkontostand) {
        id = pid;
        name = pname;
        vorname = pvorname;
        alter = palter;
        strasse = pstrasse;
        nr = pnr;
        wohnort = pwohnort;
        postleitzahl = ppostleitzahl;
        kontostand = pkontostand;
    }
    
    /* Next-Pointer setzen */
    void Person::setNext(Person *pnext) {
        next = pnext;
    }
    
    /* Person ausgeben */
    void Person::print() {
        cout << "ID: " << id << endl;
        cout << "Name: " << name << endl;
        cout << "Vorname: " << vorname << endl;
        cout << "Alter: " << alter << endl;
        cout << "Strasse: " << strasse << endl;
        cout << "Nummer: " << nr << endl;
        cout << "Wohnort: " << wohnort << endl;
        cout << "Postleitzahl: " << postleitzahl << endl;
        cout << "Kontostand: " << kontostand << endl << endl;
    }
    
    /* Next-Pointer lesen */
    Person* Person::getNext() {
        return next;
    }
    
    /* ID zurueckgeben */
    int Person::getID() {
        return id;
    }
    
    /* Wenn das Objekt geloescht wird noch eine Meldung ausgeben */
    Person::~Person() {
        cout << "Person geloescht" << endl;
    }
    
    /* Person in Datei schreiben */
    void Person::write(ofstream &out) {
        out << id << endl;
        out << name << endl;
        out << vorname << endl;
        out << alter << endl;
        out << strasse << endl;
        out << nr << endl;
        out << wohnort << endl;
        out << postleitzahl << endl;
        out << kontostand << endl;
    }
    
    /****************************************************************************/
    
    /** Klasse fuer Datenbank */
    class PersonDB {
    private:
        Person *first;
    public:
        void erfassen();
        void ausgeben();
        void sortieren();
        void suchen(int id);
        void loeschen(int id);
        void speichern();
        void laden();
    };
    
    /* Eine Person erfassen */
    void PersonDB::erfassen() {
        /* person erfassen */
        int id, alter, postleitzahl, nr;
        double kontostand;
        string name, vorname, strasse, wohnort;
    
        cout << "Neue Person." << endl << "ID: ";
        cin >> id;
        cout << "Name: ";
        cin >> name;
        cout << "Vorname: ";
        cin >> vorname;
        cout << "Alter: ";
        cin >> alter;
        cout << "Strasse: ";
        cin >> strasse;
        cout << "Nummer: ";
        cin >> nr;
        cout << "Wohnort: ";
        cin >> wohnort;
        cout << "Postleitzahl: ";
        cin >> postleitzahl;
        cout << "Kontostand: ";
        cin >> kontostand;
        cout << endl << "Daten gespeichert" << endl;
    
        /* Person mittels Constructor erstellen */
        Person *neu = new Person(id, name, vorname, alter, strasse, nr, wohnort, postleitzahl, kontostand);
    
        /* in die Liste einfuegen */
        neu->setNext(first);
        first = neu;
    }
    
    /* Alle Personen ausgeben */
    void PersonDB::ausgeben() {
        // Personen ausgeben
        Person *tmp = first;
        int anz = 0;
        while (tmp != NULL) {
            anz = anz + 1;
            cout << endl << "Person Nr. " << anz << ": " << endl;
            tmp->print();
            tmp = tmp->getNext();
        }
    }
    
    /* Alle Personen nach ID sortieren */
    void PersonDB::sortieren() {
        // Personen sortieren (Insertionsort)
        Person *sorted, *unsorted;
    
        // nix zum sortieren
        if (first == NULL) return;
    
        // Aufteilen in 2 listen
        sorted = first; // enthaelt das erste Element
        unsorted = first->getNext(); // enthaelt alle anderen Elemente
        sorted->setNext(NULL);
    
        // solange es unsortierte Personen gibt
        while (unsorted != NULL) {
            // naechsten Kandidat auswaehlen
            Person *actual = unsorted;
            unsorted = unsorted->getNext();
    
            // wir haben einen neuen Kandidaten fuer den ersten Platz
            if (actual->getID() < sorted->getID()) {
                // ersten Platz austauschen
                actual->setNext(sorted);
                sorted = actual;
            } else {
                Person *tmp = sorted;
                // solange es noch mehr Personen in der sortierten Liste hat
                // (d.h. wir sind nicht am Ende) und es noch groessere IDs weiter hinten
                // hat iterieren wir weiter
                while (tmp->getNext() != NULL && tmp->getNext()->getID() < actual->getID()) {
                    tmp = tmp->getNext();
                }
                // Element einfuegen
                actual->setNext(tmp->getNext());
                tmp->setNext(actual);
            }
        }
        // first nun noch auf die sortierte Liste zeigen lassen
        first = sorted;
        cout << "Datensatz sortiert" << endl;
    }
    
    /* Eine Person nach ID suchen */
    void PersonDB::suchen(int id) {
        Person *tmp = first;
        while (tmp != NULL && tmp->getID() != id) {
            tmp = tmp->getNext();
        }
        if (tmp != NULL && tmp->getID() == id) {
            cout << "Person wurde gefunden:" << endl;
            tmp->print();
        } else {
            cout << "Person wurde nicht gefunden" << endl;
        }
    }
    
    /* Eine Person nach ID loeschen */
    void PersonDB::loeschen(int id) {
        Person *tmp = first;
        Person *prev = NULL;
    
        // Liste traversieren und element suchen
        while (tmp != NULL) {
            if (tmp->getID() == id) {
                cout << "Person wurde gefunden:" << endl;
                tmp->print();
    
                // entweder wird das erste Element geloescht
                // oder eines aus der Liste
                if (prev == NULL) {
                    first = first->getNext();
                } else {
                    prev->setNext(tmp->getNext());
                }
    
                delete tmp;
                return; // Ruecksprung
            }
    
            // weiter in der liste
            prev = tmp;
            tmp = tmp->getNext();
        }
        cout << "Person wurde nicht gefunden" << endl;
    }
    
    /* speichert alle Personen aus der DB */
    void PersonDB::speichern() {
        /* hat es ueberhaupt Daten zum speichern? */
        if (first == NULL) return;
        Person *tmp = first;
        ofstream out;
        out.open("personen");
    
        while (tmp != NULL) {
            tmp->write(out);
            tmp = tmp->getNext();
        }
        cout << "Daten gespeichert" << endl;
    }
    
    /* laedt alle Personen aus einer Datei und fuegt sie in die DB ein */
    void PersonDB::laden() {
        ifstream in;
        in.open("personen");
        while (!in.eof()) {
            int id, alter, postleitzahl, nr;
            double kontostand;
            string name, vorname, strasse, wohnort;
    
            in >> id;
            in >> name;
            in >> vorname;
            in >> alter;
            in >> strasse;
            in >> nr;
            in >> wohnort;
            in >> postleitzahl;
            in >> kontostand;
    
            /* das speichern fuegt ein newline zuviel ein, daher bricht while nicht
               sofort ab. deshalb testen wir hier nochmals, ob wir alles einlesen
               konnten. falls nein springen wir raus */
            if (in.eof()) {
                break;
            }
    
            /* Person mittels constructor erstellen */
            Person *neu = new Person(id, name, vorname, alter, strasse, nr, wohnort, postleitzahl, kontostand);
    
            /* in die liste einfuegen */
            neu->setNext(first);
            first = neu;
        }
        cout << endl << "Datensatz erfolgreich geladen" << endl << endl;
    }
    
    /****************************************************************************/
    
    int main(int argc, char** argv) {
        {
            PersonDB mydb = PersonDB();
    
            // Personen erfassen
            bool mehrEingeben = true;
            while (mehrEingeben) {
                // Person einlesen
                cout << "Was moechtest du tun?" << endl;
                cout << "1 - Personen eingeben" << endl;
                cout << "2 - Personen sortieren" << endl;
                cout << "3 - Personen anzeigen" << endl;
                cout << "4 - Personen suchen (id)" << endl;
                cout << "5 - Personen loeschen (id)" << endl;
                cout << "6 - Personen speichern" << endl;
                cout << "7 - Personen laden" << endl;
                cout << "x - Beenden" << endl;
                char c;
                int id;
                cin >> c;
                switch (c) {
                    case 'x':
                        mehrEingeben = false;
                        break;
                    case '1':
                        mydb.erfassen();
                        break;
                    case '2':
                        mydb.sortieren();
                        break;
                    case '3':
                        mydb.ausgeben();
                        break;
                    case '4':
                        cout << "ID: ";
                        cin >> id;
                        mydb.suchen(id);
                        break;
                    case '5':
                        cout << "ID: ";
                        cin >> id;
                        mydb.loeschen(id);
                        break;
                    case '6':
                        mydb.speichern();
                        break;
                    case '7':
                        mydb.laden();
                        break;
                    default: cout << "Ungueltiger Befehl!" << endl;
                }
            }
        }
        return (EXIT_SUCCESS);
    }
    


  • Nemonisinili schrieb:

    Hab jetzt das ganze völlig neu überdacht und als verkettete Liste implementiert weil mir diese so gut gefallen 😉

    Dafür gibts std::list . :p

    Nein, ich kann ja verstehen, dass man am Anfang einiges selbst ausprobieren möchte. Falls es dir aber mal darum geht, möglichst sauber, produktiv oder fehlerfrei zu programmieren, sind die STL-Container erste Wahl. In Zukunft würde ich mir die unbedingt mal anschauen. Besonders wenns dann um binäre Bäume, Iteratorenkonzepte etc. geht, ist eine eigene Implementierung auch nicht mehr trivial.



  • Vielen Dank für Deinen Hinweis! Werde ich wohl oder übel in nächster Zukunft mal tun müssen so wie's aussieht... aber diese Aufgabe habe ich mir eigentlich nur als Prüfungsvorbereitung selber gestellt, da darin so einige Dinge vorkommen. Viel mehr muss ich noch nicht können, da ich Elektrotechnik und nicht Informatik studiere 🙂



  • ich hab nen paar fehler gefunden:

    #include <stdlib.h> //falsch!
    //==>
    #include <cstdlib> //richtig
    

    Hoffentlich steht das, was du hier gepostet hast, nicht alles in der selben Datei...

    using namespace std;
    

    Hoffentlich nicht im Header

    class Person {
    private:
        int id;
        string name;
        string vorname;
        int alter;
        string strasse;
        int nr;
        string wohnort;
        int postleitzahl;
        double kontostand;
        Person *next;
    public:
        Person(int id, string name, string vorname, int alter, string strasse, int nr, string wohnort, int postleitzahl, double kontostand);
        void setNext(Person *next);
        Person *getNext();
        void print();
        int getID();
        ~Person();
        void write(ofstream &out);
    };
    

    1. na toll - und wenn du auf nen vector umsteigen willst, musst du die ganze klasse neu schreiben...
    so was macht man nicht, dass man 2 klassen mischt....
    irgendjemand hier hatte mal geschrieben, dass man die aufgaben einer klasse genau beschreiben können muss ohne auch nur einmal und zu verwenden - ansonsten sollte man die klasse noch mal aufsplitten. natürlich gibts auch hier ausnahmen aber das ist ja echt nicht hübsch ><

    class Name
    {
        string name;
        string vorname;
    public:
        Name () {}
    //getter, setter, richtigen ctor, ...
    };
    
    class Hausnummer
    {
        size_t nr;
        char* zusatz; //wird in der Regel 0 sein
    public:
        //CTor / DTor .... (an new und delete denken)
    };
    
    class Postleitzahl
    {
        size_t postleitzahl;
    public:
    //CTor, ...
    //vor allem noch operator >> - damit die vorderen stellen ggf. mit 0en gefüllt werden
    };
    
    class Anschrift
    {
        string strasse;
        Hausnummer nr;
        string wohnort;
        Postleitzahl plz;
    public:
        //...
    };
    
    class Person {
    private:
        Name name;
        int alter;
    
        Anschrift anschrift;
    
        int id; //gehört imho nicht zu ner person - ich hab zumindest keine id... vll zu einer, auf Person aufbauenden, klasse, also:
    /*
    class ClanMember : public Person
    {
       size_t ID;
    public:
       //CTor / Getter / Setter / ...
    }
    */
    
        double kontostand; //ich würde size_t oder so als kontostand nehmen - und in Cent speichern... Dann könnte hier immerhin jeder noch 43 Millionen haben... Auch hier würde ich wieder ne kleine Klasse bauen, die für mich die Ausgabe und vll nen cast nach float etc übernimmt...
    public:
    //CTor / Getter / Setter
    };
    
    //operator ==
    //operator >> und <<
    
    Person::Person(int pid, string pname, string pvorname, int palter, string pstrasse, int pnr, string pwohnort, int ppostleitzahl, double pkontostand) {
        id = pid;
        name = pname;
        vorname = pvorname;
        alter = palter;
        strasse = pstrasse;
        nr = pnr;
        wohnort = pwohnort;
        postleitzahl = ppostleitzahl;
        kontostand = pkontostand;
    }
    

    Für so was gibts die Initialisierungsliste

    /* Person ausgeben */
    void Person::print() {
        cout << "ID: " << id << endl;
        cout << "Name: " << name << endl;
        cout << "Vorname: " << vorname << endl;
        cout << "Alter: " << alter << endl;
        cout << "Strasse: " << strasse << endl;
        cout << "Nummer: " << nr << endl;
        cout << "Wohnort: " << wohnort << endl;
        cout << "Postleitzahl: " << postleitzahl << endl;
        cout << "Kontostand: " << kontostand << endl << endl;
    }
    

    und hierfür:

    std::ostream& operator << (std::ostream &s, const Person &pers)
    {
        s << pers.GetAnschrift.GetNummer() << /*...*/;
        return s;
    }
    
    /* Person in Datei schreiben */
    void Person::write(ofstream &out) {
        out << id << endl;
        out << name << endl;
        out << vorname << endl;
        out << alter << endl;
        out << strasse << endl;
        out << nr << endl;
        out << wohnort << endl;
        out << postleitzahl << endl;
        out << kontostand << endl;
    }
    

    Was mit dem operator auch schon erledigt wäre...

    analog zum einlesen würde es auch noch was geben:

    std::istream& operator >> (std::istream &s, Person &pers)
    {
        s >> pers.Name;
        return s;
    }
    

    Den ich üblicherweise als friend der Klasse definiere...

    class PersonDB {
    private:
        Person *first;
    public:
        void erfassen();
        void ausgeben();
        void sortieren();
        void suchen(int id);
        void loeschen(int id);
        void speichern();
        void laden();
    };
    

    Für so was nimmt man eigtl std::list / std::deque / std::vector / ... irgendwas davon.
    Sieht für mich zumindest gerade so aus, als ob das das richtige für dich wäre... so ne funktionen wie erfassen sind übrigens hässlich - das macht man dann von außen:

    Nachtrag: Vll wäre auch ne std::map richtig... da könntest du deine ID als key nehmen und die person eben ganz normal ohne id speichern...

    #include "Person.h"
    
    #include <algorithm> //std::sort
    #include <iostream>
    #include <vector>
    
    int main()
    {
      std::vector <Person> personen;
    //eingabe
      for (Person tmp; std::cin >> tmp; )
      {
        personen.push_back (tmp);
      };
    //fertig - zumindest falls man nen richtiges abbruchkriterium gesagt hätte - so kommt man hier wo nie raus ^^
    
    //sortieren:
      std::sort (&personen[0], &personen[0] + personen.size());
    //fertig sortiert
    }
    

    finden/löschen/...
    auch dafür gibts fertige funktionen...
    siehe:
    http://www.cplusplus.com/reference/
    oder
    http://www.cppreference.com/wiki/
    oder
    google ^^
    falls du mal gar nicht weist, ob es das richtige/optimale ist oder dir einfach unsicher bist... weist ja, wie man hier schreibt... ^^

    PersonDB mydb = PersonDB();
    //==>
            PersonDB mydb;
    

    das hier ist

    return (EXIT_SUCCESS);
    

    das ist unnötig ^^
    sowohl die klammern um EXIT_SUCCESS als auch, dass du überhaupt nen return drin hast - die main-fkt gibt standard-mäßig (also wenn du nix anderes angibst) immer 0 (EXIT_SUCCESS ist nen makro auf 0) zurück...

    bb



  • Man kanns auch übertreiben. Gut, es sind vielleicht etwas viele Eigenschaften für eine Klasse, aber dennoch sind sie Eigenschaften einer Person. Die Klasse hat also die Aufgabe, eine Person zu beschreiben.

    Vielleicht kann man noch aufteilen in Adresse, Konto etc., aber nur, wenn das einen Vorteil bringt. Zum Beispiel damit Adressen oder Kontoangaben leichter austauschbar sind, wenn mal ein anderes System verwendet wird.

    Aber hier...

    class Postleitzahl
    {
        size_t postleitzahl;
    public:
    //CTor, ...
    //vor allem noch operator >> - damit die vorderen stellen ggf. mit 0en gefüllt werden
    };
    

    Wie soll man bei size_t die vorderen Stellen mit Nullen (nicht) füllen? Hier nimmt man einen std::string und dann hat sichs. Dafür eine eigene Klasse zu schreiben, bei der auch noch die Stream-Operatoren überladen sind, erfordert schon einen wirklich guten Grund.



  • Nexus schrieb:

    Man kanns auch übertreiben. Gut, es sind vielleicht etwas viele Eigenschaften für eine Klasse, aber dennoch sind sie Eigenschaften einer Person.

    Nummer oder Straße oder ID sind also die Eigenschaften einer Person?
    Naja - man kann es machen - aber es sind halt echt zu viele Eigenschaften...

    Nexus schrieb:

    Wie soll man bei size_t die vorderen Stellen mit Nullen (nicht) füllen? Hier nimmt man einen std::string und dann hat sichs. Dafür eine eigene Klasse zu schreiben, bei der auch noch die Stream-Operatoren überladen sind, erfordert schon einen wirklich guten Grund.

    meinte btw den operator << ... ist mir nicht aufgefallen, dass es der falsche war ><

    Ne - weils einfach mal ne PLZahl ist - und nen string einfach mal nix da zu suchen hat...
    mal ein paar gründe:
    - "01234" != "1234"
    - unzählige prüfungen für die eingabe (nur zahlen eingegeben, richtige länge angegeben, ...)
    - vergleiche sind langsamer -> nach plz sortieren stinkt
    - mind. 6 * mind. 1 Byte vs. 4 (bzw. 😎 Byte

    bb



  • unskilled schrieb:

    Ne - weils einfach mal ne PLZahl ist - und nen string einfach mal nix da zu suchen hat...
    mal ein paar gründe:
    - "01234" != "1234"

    hab gerde mit size_t experimentiert.
    da war auch 01234 != 1234 😮 🤡



  • unskilled schrieb:

    Nummer oder Straße oder ID sind also die Eigenschaften einer Person?
    Naja - man kann es machen - aber es sind halt echt zu viele Eigenschaften...

    Ja, wie ich sagte, gewisse Aufteilungen können schon sinnvoll sein, aber ich würde nicht von vorneweg sagen, es wäre schlecht ohne.

    unskilled schrieb:

    Ne - weils einfach mal ne PLZahl ist - und nen string einfach mal nix da zu suchen hat...

    Ach ja? Ich würde eher sagen, ein Integer hat da nichts zu suchen. Man muss schliesslich keine einzige Rechnung damit betreiben. Noch ein paar andere Gegengründe:

    unskilled schrieb:

    - "01234" != "1234"

    Gerade das spricht doch für Strings. Wie willst du einen size_t speichern, der mit einer Null anfängt? Oder den für ein anderes System, bei dem die Grösse von size_t nicht ausreicht oder nicht nur Ziffern vorkommen?

    unskilled schrieb:

    - unzählige prüfungen für die eingabe (nur zahlen eingegeben, richtige länge angegeben, ...)

    Und wieso sollten diese Prüfungen bei einem size_t wegfallen?

    unskilled schrieb:

    - vergleiche sind langsamer -> nach plz sortieren stinkt
    - mind. 6 * mind. 1 Byte vs. 4 (bzw. 😎 Byte

    Performance- und Speicherplatzwahn auf Kosten der Funktionalität? Das würde ich nicht wollen...



  • Nexus schrieb:

    unskilled schrieb:

    - "01234" != "1234"

    Gerade das spricht doch für Strings. Wie willst du einen size_t speichern, der mit einer Null anfängt? Oder den für ein anderes System, bei dem die Grösse von size_t nicht ausreicht oder nicht nur Ziffern vorkommen?

    Jup. Muss ja nicht unbedingt eine Zahl sein.
    z.B: http://de.wikipedia.org/wiki/Postleitzahl_(Vereinigtes_Königreich)



  • LOL, ich hatte schon Angst dass ich auf meine Problematik gar keine Antwort kriege weil alle anderen Posts welche nach meinem gepostet wurden viel schneller beantwortet wurden... Und jetzt bin ich völlig überfordert mit all euren Tricks & Tipps 🙂 Ihr seid schon genial, aber sooo gut muss das auch wieder nicht sein 😉 Ich werde nach der morgigen Prüfung mir euren Code noch genauer ansehen und mich etwas über diese Möglichkeiten informieren 🙂
    Ich hoffe ihr verzeiht mir dass ich trotz mehrmaligen Lesens eurer Hinweise noch nicht weiss, welche dieser Möglichkeiten jetzt die beste von allen ist... vermutlich ab einem gewissen Grad Geschmackssache denke ich. Aber dass meine Version unschön ist, dessen bin ich mir zumindest bewusst 😉



  • volkard schrieb:

    hab gerde mit size_t experimentiert.
    da war auch 01234 != 1234 😮 🤡

    oO

    @nexus: imho ging es nur um deutsche anschriften / plz / ...
    ich hab zumindest keine länderkennung oä in der klasse gesehen 😛

    und im deutschen ist es nun mal so, dass die PLZ "01234" gleich der "1234" entspricht...
    es ist zwar nicht gerade üblich, 1234 zu schreiben aber imho auch nicht verkehrt...

    Und wieso sollten diese Prüfungen bei einem size_t wegfallen?

    size_t plz;
    do    {
       std::cin >> plz;
    } while ((plz > 99999) || (!plz));
    

    im Gegensatz zu std::string, bei dem man die länge überprüfen muss und extra noch jede stelle darauf, ob es eine zahl ist...

    btw:
    die klasse ist auch auf vor- und nachname beschränkt - was ist, wenn jmd nen dritten name hat?

    naja - wie auch immer... nimm halt nen string

    bb



  • unskilled schrieb:

    und im deutschen ist es nun mal so, dass die PLZ "01234" gleich der "1234" entspricht...
    es ist zwar nicht gerade üblich, 1234 zu schreiben aber imho auch nicht verkehrt...

    Hm, naja. Bei einer Zahl muss man jedes Mal bei der Ausgabe wieder an die führenden Nullen denken. Da finde ich eine einheitliche Formatierung mit Zeichenketten gleicher Länge besser.

    unskilled schrieb:

    size_t plz;
    do    {
       std::cin >> plz;
    } while ((plz > 99999) || (!plz));
    

    im Gegensatz zu std::string, bei dem man die länge überprüfen muss und extra noch jede stelle darauf, ob es eine zahl ist...

    Und was passiert, wenn man in die Konsole "askfdlaö" eingibt?

    Abgesehen davon, wieso sollte man die PLZ hier überhaupt prüfen? Wenn, dann müsste man wohl jeden Ort gespeichert haben (die Zahlen sind ja nicht schön aufgezählt). Und andere Attribute prüft man schliesslich auch nicht.

    Naja, es ginge vielleicht schon knapp mit size_t . Aber damit ist man unflexibel und eine eigene Klasse dafür zu überladen, obwohl es mit std::string gut geht, finde ich nicht angebracht...



  • Nexus schrieb:

    Naja, es ginge vielleicht schon knapp mit size_t . Aber damit ist man unflexibel und eine eigene Klasse dafür zu überladen, obwohl es mit std::string gut geht, finde ich nicht angebracht...

    endlich sagt einer den rechten weg!
    und dann lehnt er ihn ab. 😞

    klar nen eigenen typ. nix anderes darf gemacht werden.
    anfangs reicht durchaus

    typedef std::string Postleitzahl.
    

    keine string-spezifischen und unpostleitzahligen sachen sachen wie .begin() benutzen. und sich damit den weg freihalten, die postleitzahl beliebig anders zu implementieren.

    sowas geht auch:

    struct Postleitzahl
    {
       int plz;
       friend ostream& operator<<(ostream& out){
          return out<<setfill('0')<<setw(5)<<plz;
       }
       friend bool operator<(Postleitzahl a,Postleitzahl b){
          return a.plz<b.plz;
       }
    }
    

    ist egal. aber es MUSS einen eigenen typen gehen. wer von euch weiß, wann die post wieder die postleitzahlen umstellt? ich jedenfalls nicht und bevorzuge in solchen sachen einen ganz defensiven programmirstil.



  • volkard schrieb:

    sowas geht auch:

    struct Postleitzahl
    {
       int plz;
       friend ostream& operator<<(ostream& out){
          return out<<setfill('0')<<setw(5)<<plz;
       }
       friend bool operator<(Postleitzahl a,Postleitzahl b){
          return a.plz<b.plz;
       }
    }
    

    Und genau so habe ich es doch auch gesagt - nur, dass ich statt int size_t genommen hatte (weil mir nicht bewusst ist, dass es negative PLZs gibt :-P)...

    unskilled schrieb:

    class Postleitzahl
    {
    size_t postleitzahl;
    public:
    //CTor, ...
    //vor allem noch operator >> - damit die vorderen stellen ggf. mit 0en gefüllt werden
    };

    unskilled schrieb:

    meinte btw den operator << ... ist mir nicht aufgefallen, dass es der falsche war ><

    Mit Einlesen unter der Annahme, dass alle Zahlen von 1 bis 99999 eine gültige PLZ ergeben:

    istream& operator << (istream& in, Postleitzahl &plz)
    {
       plz.plz = 0;
       do
       {
          in >> plz.plz;
       } while ((!plz.plz) || (plz.plz > 99999));
       return in;
    }
    

    allerdings hätte das ganze nen prob:
    wenn man das ganze in ne datei speichert und jmd eine postleitzahl ändert (ungültig macht), dann werden (im besten fall nur) 2 Einträge gemischt.
    also würde man die überprüfung wahrscheinlich doch ganz weglassen.
    wenn man trotzdem nicht auf die überprüfung verzichten möchte:
    (ich weiß nur nicht, ob das hier üblich bzw. elegant ist oder doch eher falsch weil keine sau auf die idee kommt, dass der operator << ne exception werfen darf)

    istream& operator << (istream& in, Postleitzahl &plz)
    {
       in >> plz.plz;
       if ((!plz.plz) || (plz.plz > 99999))
          throw /*...*/;
       return in;
    }
    

    also würde ich dann dazu gehen, die eingabe nur so zu implementieren:

    istream& operator << (istream& in, Postleitzahl &plz)
    {
       return in >> plz.plz;
    }
    
    bool Postleitzahl::good() const
    {
      return ((plz) && (plz < 99999));
    }
    

    falls es nötig wäre könnte man das ganze auch noch mit plz-tabellen erweitern, die man halt iwo abspeichert evtl cacht oder ne extra klasse zum prüfen macht...

    ich weiß nicht, wie man das (in der Praxis) macht, dass personen versch. nationen in das system könnten, aber evtl so:

    class BasePLZ
    {
    public:
      bool bad() const {return !good();}
      virtual bool good() const  {return true;}
    
      virtual ~BasePLZ() = 0;
    };
    
    template <typename T>
    class Postleitzahl : public BasePLZ
    {
      T plz;
    public:
      Postleitzahl(T _plz) : plz(_plz) {}
    
      virtual T Get() const {return T;}
    };
    
    template <typename T>
    ostream& operator << (ostream& out, typename const Postleitzahl<T> &plz)
    {
      return out << plz.plz;
    }
    
    template <typename T>
    istream& operator >> (ostream& in, typename Postleitzahl<T> &plz)
    {
      return in >> plz.plz;
    }
    
    class GerPLZ : public Postleitzahl <size_t>
    {
     public:
      GerPLZ(size_t _plz) : Postleitzahl(_plz) {}
    };
    
    struct strange_plz
    {
      char a[4];
      char b;
      std::string c;
    //CTor
    }; //was auch immer
    
    //operator << / >>
    
    class StrangeCountryPLZ : public Postleitzahl <strange_plz>
    {
     public:
      StrangeCountryPLZ (const strange_plz& _plz) : Postleitzahl(_plz) {}
    };
    

    weiß nicht, ob ich hier jz iwo noch fehler drin habe, aber so würde ich es wohl in echt versuchen zu implementieren (nur das Land iwie als enum und dann halt so lang dran rum basteln, bis alles so funktioniert, wie ich es gern verwenden würde)

    bb



  • volkard schrieb:

    ...hab gerde mit size_t experimentiert.
    da war auch 01234 != 1234 😮 🤡

    01234 == Oktalzahl
    1234 == Dezimalzahl
    🕶



  • unskilled schrieb:

    class GerPLZ : public Postleitzahl <size_t>
    

    wichtig ist hier, das ziel nicht aus den augen zu verlieren. ich plädiere dafür, die Postleitzahlenklasse wirklich einfach zu lassen, string oder ganz dummer wrapper um int oder unsigned int (nicht size_t, weil size_t auf 64-bit-compilern auf einmal auf 64 bits aufgezpgen wird.). die verlockung ist groß, jetzt noch lustige sachen einzubauen, weil es auf einmal geht. aber ich würde lieber bescheiden weiterprogrammieren wie zuvor. kann mir nicht wirklich vorstellen, daß die sekretäre mit den features überhaupt umgehen wollen.



  • volkard schrieb:

    nicht size_t, weil size_t auf 64-bit-compilern auf einmal auf 64 bits aufgezpgen wird.

    und wo ist der nachteil?
    so lang man nichts binär speichert ist das ja alles np...
    aber ja - kannst au unsigned int oder was au immer nehmen

    bb


Anmelden zum Antworten