Warum Iteratoren anstelle von Array-Indizes verwenden?

Lesezeit: 9 Minuten

Warum Iteratoren anstelle von Array Indizes verwenden
Jason Baker

Nehmen Sie die folgenden zwei Codezeilen:

for (int i = 0; i < some_vector.size(); i++)
{
    //do stuff
}

Und das:

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
    some_iterator++)
{
    //do stuff
}

Mir wurde gesagt, dass der zweite Weg bevorzugt wird. Warum genau ist das so?

  • Der zweite Weg wird bevorzugt, wenn Sie sich ändern some_iterator++ zu ++some_iterator. Nach dem Inkrement wird ein unnötiger temporärer Iterator erstellt.

    – Jason

    3. Januar 2010 um 15:21 Uhr

  • Solltest du auch mitbringen end() in die Erklärungsklausel.

    – Leichtigkeitsrennen im Orbit

    31. Mai 2011 um 15:11 Uhr

  • @Tomalak: Jeder, der eine C++-Implementierung mit einem ineffizienten verwendet vector::end hat wahrscheinlich schlimmere Probleme, als sich Gedanken darüber zu machen, ob es aus den Schlaufen gezogen wird oder nicht. Ich persönlich bevorzuge Klarheit – wenn es um einen Anruf ging find bei der Kündigungsbedingung würde ich mir allerdings Sorgen machen.

    – Steve Jessop

    31. Mai 2011 um 17:16 Uhr


  • @Tomalak: Dieser Code ist nicht schlampig (na ja, das Post-Inkrement vielleicht), er ist prägnant und klar, soweit C ++ – Iteratoren Prägnanz zulassen. Das Hinzufügen weiterer Variablen erhöht den kognitiven Aufwand zugunsten einer vorzeitigen Optimierung. Das ist schlampig.

    – Steve Jessop

    31. Mai 2011 um 17:19 Uhr


  • @Tomalak: Es ist verfrüht, wenn es kein Engpass ist. Ihr zweiter Punkt scheint mir absurd, da der richtige Vergleich nicht zwischen ist it != vec.end() und it != endes ist zwischen (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) und (vector<T>::iterator it = vec.begin(), end = vec.end(); it != end; ++it). Ich brauche die Zeichen nicht zu zählen. Bevorzugen Sie auf jeden Fall eines gegenüber dem anderen, aber die Meinungsverschiedenheit anderer Leute mit Ihrer Präferenz ist keine “Schludrigkeit”, sondern eine Präferenz für einfacheren Code mit weniger Variablen und daher weniger Anlass zum Nachdenken beim Lesen.

    – Steve Jessop

    31. Mai 2011 um 17:26 Uhr


1646899811 912 Warum Iteratoren anstelle von Array Indizes verwenden
wilhelmell

Die erste Form ist nur effizient, wenn vector.size() eine schnelle Operation ist. Das gilt für Vektoren, aber beispielsweise nicht für Listen. Und was hast du vor, innerhalb des Körpers der Schleife zu tun? Wenn Sie vorhaben, auf die Elemente wie in zuzugreifen

T elem = some_vector[i];

dann machen Sie die Annahme, dass der Behälter hat operator[](std::size_t) definiert. Auch dies gilt für Vektoren, aber nicht für andere Container.

Der Einsatz von Iteratoren bringt Sie näher heran Containerunabhängigkeit. Sie machen keine Annahmen über die Fähigkeit zum wahlfreien Zugriff oder schnell size() Operation, nur dass der Container Iteratorfähigkeiten hat.

Sie könnten Ihren Code weiter verbessern, indem Sie Standardalgorithmen verwenden. Je nachdem, was Sie erreichen möchten, können Sie sich für die Verwendung entscheiden std::for_each(), std::transform() und so weiter. Indem Sie anstelle einer expliziten Schleife einen Standardalgorithmus verwenden, vermeiden Sie es, das Rad neu zu erfinden. Ihr Code ist wahrscheinlich effizienter (vorausgesetzt, der richtige Algorithmus wird gewählt), korrekt und wiederverwendbar.

  • Außerdem haben Sie vergessen, dass Iteratoren Dinge wie Fail-Fast tun können, sodass Sie davon erfahren, wenn es eine gleichzeitige Änderung an der Struktur gibt, auf die Sie zugreifen. Mit einer ganzen Zahl geht das nicht.

    – Marcin

    21. Oktober 2008 um 22:25 Uhr

  • Das verwirrt mich: “Das gilt für Vektoren, aber beispielsweise nicht für Listen.” Warum? Jeder mit einem Gehirn wird ein behalten size_t Mitgliedsvariable im Auge behalten size().

    – GManNickG

    13. Juli 2009 um 7:39 Uhr

  • @GMan – In fast allen Implementierungen ist size() für Listen genauso schnell wie für Vektoren. Bei der nächsten Version des Standards muss dies der Fall sein. Das eigentliche Problem ist die Langsamkeit des Rückzugs nach Position.

    – Daniel Earwicker

    13. Juli 2009 um 7:41 Uhr

  • @GMan: Das Speichern der Listengröße erfordert, dass das Aufteilen und Spleißen der Liste O (n) anstelle von O (1) ist.

    Roger Pate

    6. November 2010 um 15:38 Uhr

  • In C++0x ist die size() Die Member-Funktion muss für alle Container, die sie unterstützen, eine konstante Zeitkomplexität aufweisen, einschließlich std::list.

    – James McNellis

    6. November 2010 um 16:43 Uhr


Warum Iteratoren anstelle von Array Indizes verwenden
Mark Lösegeld

Es ist Teil des modernen C++-Indoktrinationsprozesses. Iteratoren sind die einzige Möglichkeit, die meisten Container zu iterieren, also verwenden Sie sie auch mit Vektoren, nur um sich in die richtige Denkweise zu versetzen. Im Ernst, das ist der einzige Grund, warum ich es mache – ich glaube nicht, dass ich jemals einen Vektor durch eine andere Art von Container ersetzt habe.


Wow, das wird nach drei Wochen immer noch heruntergestimmt. Ich denke, es zahlt sich nicht aus, ein wenig ironisch zu sein.

Ich denke, der Array-Index ist besser lesbar. Es entspricht der Syntax, die in anderen Sprachen verwendet wird, und der Syntax, die für altmodische C-Arrays verwendet wird. Es ist auch weniger ausführlich. Effizienz sollte eine Wäsche sein, wenn Ihr Compiler gut ist, und es gibt sowieso kaum Fälle, in denen es darauf ankommt.

Trotzdem verwende ich immer noch häufig Iteratoren mit Vektoren. Ich glaube, dass der Iterator ein wichtiges Konzept ist, also fördere ich ihn, wann immer ich kann.

  • C++-Iteratoren sind auch konzeptionell fürchterlich kaputt. Bei Vektoren wurde ich nur erwischt, weil der Endzeiger tatsächlich Ende + 1 (!) ist. Für Streams ist das Iterator-Modell einfach surreal – ein imaginäres Token, das nicht existiert. Ebenso für verkettete Listen. Das Paradigma macht nur Sinn für Arrays und dann nicht viel. Warum brauche ich zwei Iterator-Objekte, nicht nur eines …

    – Abstimmbar

    21. Januar 2016 um 5:14 Uhr

  • @aberglas sie sind überhaupt nicht kaputt, man ist sie einfach nicht gewohnt, weshalb ich dafür plädiere, sie auch dann zu verwenden, wenn man es nicht muss! Halboffene Bereiche sind ein gängiges Konzept, und Wächter, auf die niemals direkt zugegriffen werden soll, sind ungefähr so ​​alt wie die Programmierung selbst.

    – Markieren Sie Lösegeld

    21. Januar 2016 um 6:25 Uhr

  • Schauen Sie sich die Stream-Iteratoren an und denken Sie darüber nach, was == pervers gemacht wurde, um in das Muster zu passen, und sagen Sie mir dann, dass die Iteratoren nicht kaputt sind! Oder für verknüpfte Listen. Selbst für Arrays ist die Angabe eines nach dem Ende eine kaputte Idee im C-Stil – ein Zeiger auf das Never Never. Sie sollten wie Java oder C# oder die Iteratoren einer anderen Sprache sein, mit einem erforderlichen Iterator (anstelle von zwei Objekten) und einem einfachen Endtest.

    – Abstimmbar

    1. Februar 2016 um 9:34 Uhr

  • @MarkRansom Cpp-Iteratoren sind Crop und ich habe jahrelange Cpp, um dies zu untermauern, und bin zuversichtlich in Bezug auf diese Aussage. Ich werde sie weiterhin in for-Schleifen verwenden; oder wenn ich gezwungen bin, sie zu benutzen. Aber gezwungen zu sein bedeutet nicht, dass sie nicht schlecht und nicht intuitiv sind. Sie sind zumindest auf den Referenzseiten und in Ihren Cpp-Header-Dateien nicht richtig dokumentiert.

    – Benutzer13947194

    28. Dezember 2021 um 20:49 Uhr

  • @ user13947194 Das war mein eigentlicher Punkt – wenn Iteratoren nicht intuitiv sind, verwenden Sie sie nicht genug!

    – Markieren Sie Lösegeld

    29. Dezember 2021 um 0:52 Uhr

weil Sie Ihren Code nicht an die bestimmte Implementierung der some_vector-Liste binden. Wenn Sie Array-Indizes verwenden, muss es sich um eine Art Array handeln. Wenn Sie Iteratoren verwenden, können Sie diesen Code für jede Listenimplementierung verwenden.

  • Die Schnittstelle std::list bietet absichtlich keinen Operator an[](size_t n), weil es O(n) wäre.

    – MSalter

    26. September 2008 um 11:41 Uhr

1646899812 538 Warum Iteratoren anstelle von Array Indizes verwenden
Asterit

Stellen Sie sich vor, some_vector wird mit einer verketteten Liste implementiert. Dann erfordert das Anfordern eines Elements an der i-ten Stelle, dass i Operationen durchgeführt werden, um die Liste von Knoten zu durchlaufen. Wenn Sie jetzt einen Iterator verwenden, wird er sich im Allgemeinen nach besten Kräften bemühen, so effizient wie möglich zu sein (im Fall einer verknüpften Liste behält er einen Zeiger auf den aktuellen Knoten bei und setzt ihn bei jeder Iteration fort, was nur eine erfordert Einzelbetrieb).

Es bietet also zwei Dinge:

  • Abstraktion der Verwendung: Sie möchten nur einige Elemente iterieren, es ist Ihnen egal, wie Sie dies tun
  • Leistung

Ich werde hier der Anwalt des Teufels sein und keine Iteratoren empfehlen. Der Hauptgrund dafür ist, dass der gesamte Quellcode, an dem ich gearbeitet habe, von der Entwicklung von Desktop-Anwendungen bis hin zur Spieleentwicklung, weder Iteratoren erforderlich ist. Die ganze Zeit waren sie nicht erforderlich, und zweitens machen die versteckten Annahmen und Code-Chaos und Debugging-Albträume, die Sie mit Iteratoren bekommen, sie zu einem Paradebeispiel dafür, sie nicht in Anwendungen zu verwenden, die Geschwindigkeit erfordern.

Sogar aus Wartungsgründen sind sie ein Chaos. Es liegt nicht an ihnen, sondern an all dem Aliasing, das hinter den Kulissen passiert. Woher weiß ich, dass Sie keine eigene virtuelle Vektor- oder Array-Liste implementiert haben, die etwas völlig anderes als die Standards macht? Weiß ich, welcher Typ gerade jetzt zur Laufzeit ist? Haben Sie einen Operator überladen, hatte ich keine Zeit, Ihren gesamten Quellcode zu überprüfen. Verdammt, weiß ich überhaupt, welche Version der STL Sie verwenden?

Das nächste Problem, das Sie mit Iteratoren haben, ist die undichte Abstraktion, obwohl es zahlreiche Websites gibt, die dies ausführlich mit ihnen diskutieren.

Sorry, ich habe und habe noch keinen Sinn in Iteratoren gesehen. Wenn sie die Liste oder den Vektor von Ihnen abstrahieren, obwohl Sie eigentlich bereits wissen sollten, mit welchem ​​Vektor oder welcher Liste Sie es zu tun haben, wenn Sie dies nicht tun, werden Sie sich nur auf einige großartige Debugging-Sitzungen in der Zukunft einstellen.

Warum Iteratoren anstelle von Array Indizes verwenden
Schwarztempel

Möglicherweise möchten Sie einen Iterator verwenden, wenn Sie dem Vektor Elemente hinzufügen/entfernen, während Sie darüber iterieren.

some_iterator = some_vector.begin(); 
while (some_iterator != some_vector.end())
{
    if (/* some condition */)
    {
        some_iterator = some_vector.erase(some_iterator);
        // some_iterator now positioned at the element after the deleted element
    }
    else
    {
        if (/* some other condition */)
        {
            some_iterator = some_vector.insert(some_iterator, some_new_value);
            // some_iterator now positioned at new element
        }
        ++some_iterator;
    }
}

Wenn Sie Indizes verwenden, müssten Sie Elemente im Array nach oben/unten mischen, um die Einfügungen und Löschungen zu verarbeiten.

Trennung von Bedenken

Es ist sehr schön, den Iterationscode von dem „Kern“ der Schleife zu trennen. Es ist fast eine Designentscheidung.

In der Tat bindet Sie die Iteration nach Index an die Implementierung des Containers. Wenn Sie den Container nach einem Anfangs- und End-Iterator fragen, wird der Schleifencode für die Verwendung mit anderen Containertypen aktiviert.

Auch in der std::for_each Weg, du Sagen Sie der Sammlung, was sie tun soll, anstatt zu fragen es etwas über seine Interna

Der 0x-Standard wird Closures einführen, die diesen Ansatz viel einfacher zu handhaben machen – sehen Sie sich zB die Ausdruckskraft von Ruby an [1..6].each { |i| print i; }

Leistung

Aber vielleicht ist ein viel übersehenes Problem die Verwendung von for_each Ansatz bietet die Möglichkeit, die Iteration parallelisieren zu lassen – die Intel-Threading-Blöcke kann den Codeblock über die Anzahl der Prozessoren im System verteilen!

Hinweis: Nach der Entdeckung der algorithms Bibliothek und vor allem foreach, habe ich zwei oder drei Monate damit verbracht, lächerlich kleine Hilfsoperatorstrukturen zu schreiben, die Ihre Entwicklerkollegen verrückt machen werden. Nach dieser Zeit bin ich zu einem pragmatischen Ansatz zurückgekehrt – kleine Schleifenkörper verdienen nichts foreach nicht mehr 🙂

Eine unverzichtbare Referenz zu Iteratoren ist das Buch “Erweiterte STL”.

Die GoF haben am Ende des Iterator-Musters einen winzig kleinen Absatz, der über diese Art der Iteration spricht; es wird ein “interner Iterator” genannt. Guck mal Hierzu.

987030cookie-checkWarum Iteratoren anstelle von Array-Indizes verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy