iterator::it



  • Icn fange einfach direkt mit meiner Frage an: was bringt es diesen mit einzubeziehen welche Funktion hat das in einem Quelltext? Habe dazu auch ein Besipiel gefunden:

    [code="cpp"]void Ausgabe( std::vector<int> &mitarbeiternummern, Mitarbeiterdatenbank &datenbank ) //Funktion zur Ausgabe der gespeichrten Informationen
    {
    std::vector<int>::iterator it;
    it=mitarbeiternummern.begin();
    while (it != mitarbeiternummern.end()) //while-Schleife sucht in Datenbak nach Mitarbeiternummer
    {
    datenbank.ausgabeMitarbeiterinfos(*it); //ruft Funktion aus Datenbank.h auf (aus Klasse Mitarbeiter)
    it++;
    }
    }

    Ih habe auch schon im Internet geschaut jedoch hab ich das noch nicht ganz verstanden, könnte mir das jemand anhand dieses Beispiels erklären? 🙂 😕



  • killerbiene1337 schrieb:

    könnte mir das jemand anhand dieses Beispiels erklären?

    Da ist doch deine Hausaufgabe.



  • [quote]Da ist doch deine Hausaufgabe.

    Ähm nein, ich würde gerne selbst etwas ähnliches schreiben, jedoch möchte ich nichts schreiben, was ich nicht selbst verstehe.



  • killerbiene1337 schrieb:

    Ähm nein, ich würde gerne selbst etwas ähnliches schreiben, jedoch möchte ich nichts schreiben, was ich nicht selbst verstehe.

    Klar.

    Ein anständiges Anfänger C++ Buch erklärt Container und Iteratoren.



  • killerbiene1337 schrieb:

    Icn fange einfach direkt mit meiner Frage an: was bringt es diesen mit einzubeziehen welche Funktion hat das in einem Quelltext? Habe dazu auch ein Besipiel gefunden:

    Wo hast du dieses Beispiel denn gefunden? Es enthält einige Dinge, die man anders machen könnte order sollte.

    void Ausgabe( std::vector<int> &mitarbeiternummern, Mitarbeiterdatenbank &datenbank )
    

    Beide Parameter scheinen Eingabeparameter zu sein. Warum sind diese nicht "const"?

    std::vector<int>::iterator it;
        it=mitarbeiternummern.begin();
    

    Warum wird das hier nicht in einem Abwasch gemacht? Es ist tendenziell schlecht, eine Variable ohne Initialisierung bekannt zu machen. Also stattdessen:

    auto it = mitarbeiternummern.begin();
    

    Allerdings benötigen wir diese Variable eigentlich überhaupt nicht. Wozu die große while-Schleife? Am besten kürzen wir alles zusammen auf folgenden Dreizeiler:

    for (int ma : mitarbeiternummern) {
        datenbank.ausgabeMitarbeiterinfos(ma);
    }
    

    Ich weiß nicht, wo du den Code her hast, aber er ist viel zu kompliziert.


  • Global Moderator

    Der Sinn dahinter ist Abstraktion. Wobei das hier leider nicht besonders weit getrieben wurde, wodurch der Sinn dahinter wohl nicht klar wird.

    Was wäre denn die Alternative? Eine Schleife mit Zähler in der Art von

    for(size_t i = 0; i < mitarbeiternummern.size(); ++i)
      mach_irgendwas_mit(mitarbeiternummern);
    

    *Das ist ja auch schön und gut, ist aber ziemlich Vector-spezifisch. Mit einer std::list oder einem std::set würde das nicht funktionieren. Obwohl diese Container auch fähig werden, die hier gewünschte Funktionalität "gehe alle Elemente durch" zu leisten. Wenn man das so schreibt, kann man den Code nirgendwo wiederverwenden und man muss mehr Arbeit leisten, wenn man später mal feststellt, dass ein Vector vielleicht doch nicht so die optimale Wahl war und man stattdessen etwas anderes haben möchte.

    Jetzt ist dein Codebeispiel natürlich immer noch sehr vector-spezifisch, was ich ja oben schon bemängelt hatte. Denn so etwas wie

    std::vector<int>::iterator it;
    it=mitarbeiternummern.begin();
    

    geht natürlich auch nur dann wenn mitarbeiternummern ein vector<int> ist. Es scheitert sogar schon, wenn man zwar dabei verbleibt, dass vector eine gute Wahl ist, aber man merkt, dass man mit Mitarbeiternummern doch nicht rechnen möchte und stattdessen ein string als Typ besser geeignet wäre.

    Unabhängiger vom Typ von mitarbeiternummern wäre:

    auto it = mitarbeiternummern.begin();
    

    Das geht dann mindestens schon einmal mit allen STL-Containern und STL-kompatiblen Containern. Noch allgemeiner, so dass es auch für Arrays funktioniert (die keine begin-Methode haben):

    auto it = begin(mitarbeiternummern);
    

    Das geht dann schon mit allem, was auch nur irgendwie containerartig ist.Und das ohne jeden Nachteil gegenüber den anderen Varianten. Aber noch besser: Lassen wir doch den Benutzer entscheiden, was die Konzepte "Anfang" und "Ende" für ihn bedeuten:

    template<typename Iterator> void Ausgabe(Iterator mitarbeiternummern_begin, Iterator mitarbeiternummern_end, Mitarbeiterdatenbank &datenbank )
    

    Das ist dann die absolute Freiheit. Da geht alles rein, was auch nur irgendwie ein Anfang und ein Ende hat. Da kann der Benutzer dann zum Beispiel auch einen istream_iterator<int> reingeben, um die Mitarbeiternummern ohne unnötiges Zwischenspeichern direkt aus der Quelle an die Funktion zu übergeben.

    Nicht umsonst ist das letzte Beispiel die Art und Weise, wie die ganzen Standardalgorithmen in der STL geschrieben sind. Und du solltest das möglichst auch anstreben.

    PS: Bezüglich Performance: Wie schon gesagt ist das alles ohne Nachteil, da es alles nur Syntaxzucker ist. Der Code ist stets der gleiche. Das einzige Beispiel, das wirklich anderen Code erzeugen könnte ist das allererste mit der Zählschleife. Die Zählschleife ist in der Regel ein kleines bisschen langsamer, da dort bei jedem Zugriff erst ausgerechnet werden muss, an welche Position der Zugriff im Speicher gehen soll, wohingegen der Iterator diese Positionsinformation [i]ist*.
    Das kommt daher, weil man mit den Indexzugriffen prinzipiell auch mehr machen könnte, als nur der Reihenfolge nach durch etwas durchzugehen. Aber immer wenn man durch irgendetwas durchgehen möchte (=iterieren), dann ist man gut beraten, auch Iteratoren dafür zu benutzen.



  • Vielen Dank, du hast mir sehr geholfen 🙂
    Dann werde ich mich mal dran setzten und das möglichst anders schreiben 😃