iterator vs. []-operator
-
mmhh, also ich hab mal gelesen das du mit dem [] operator glaub ich eine multiplikation oder adition mehr brauchst als mit einem iterator. das ist aber mittlerweile schon so lange her das ich das nicht mehr zusammen bekomme..
ich hab mir auch mal benchmarks dazu angesehn und es scheint das der iterator doch etwas schneller ist.
aber der unterschied ist ziemlich gering...ich versuchs mal
array[3]
wird erst auf den anfang gesetzt und dann 3 elemente weiter gegangen also hast du array+3*sizeof(type)
und dann noch derefenziertnun ist aber ein iterator schon ein zeiger auf das element selbst also wird nur derefenziert
wie du siehst ist da schon ein unterschied.ich hebe aber keinen anspruch auf richtigkeit

-
Nunja, der Iterator muss irgendwo aber auch aufs nächste Element gesetzt werden. Da ein vector direkten Zugriff auf alle Indizes erlaubt macht es keinen Unterschied ob Du eins oder drei auf den Zeiger addierst

EDIT:
Bei einer Million Zugriffe auf denselben Index kommt die Addition dafür immer zum Tragen, beim Iterator nur immer dann wenn auf den folgenden Index gewechselt wird.
-
Bei der Benutzung mittels eines Index werden zwei Additionen ausgeführt, einmal für die Zählvariable (i++) und einmal für den Zugriff auf das Array (keys[i] = keys+i).
Jedoch benutze ich selber auch meist die Index-Variante, da mir die Schreibarbeit für z.B.for ( std::vector<int>::const_iterator it = keys.begin(); it != keys.end(); ++it )zu viel ist.
Aber wenn es bald(!?) im neuen C++0x Standard, die auto-Benutzung geben wird, dann werde ich es wohl durchgehend benutzen.Bei einem allgemeinen template sollte man natürlich die Iterator-Variante nutzen, da z.B. list keinen Index-Zugriff hat.
-
Schreibarbeit ist kein Argument für Codequalität.
Wenn ich stl-Container verwende, definiere ich mir in aller Regel einen Typedef, so daß ich für den Container-Typ einen dokumentierenden Namen habe. Beispielsweise:
typedef std::vector<int> KeysType; KeysType keys; for (KeysType::const_iterator it = keys.begin(); it != keys.end(); ++it )Das schöne ist halt, daß man iteratoren immer verwenden kann. Da bekommt der Code auch ein einheitliches Aussehen.
Tntnet
-
Th schrieb:
...
Jedoch benutze ich selber auch meist die Index-Variante, da mir die Schreibarbeit für z.B.for ( std::vector<int>::const_iterator it = keys.begin(); it != keys.end(); ++it )zu viel ist.
Naja, soooo viel kürzer ist:
for ( int it = 0; it != keys.end(); ++it )auch nicht (mal abgesehen davon, dass bei jeder Verwendung der Index-Variante immer noch 1 eckige Klammer und die Indexvariable dazukommt
und (min.) der Containertyp sinnvollerweise ein typedef ist).Der []-operator hat einfach einen ganz klar definierten Zweck: Random-Access.
Man sollte ihn IMO deswegen auch nur da verwenden, wo man ihn braucht. Die gesparte Tipparbeit hat man nämlich bei der ersten "Codeanpassung" ganz schnell wieder verbraten, wenn man- Indexfehler suchen und/oder
- man erstemal alle Codestellen durchgehen muss, ob hier wirklich RandomAccess gemeint ist (z.B. wenn ein anderer Containertype notwendig wird, der keinen operator[] hat).
BTW: std::for_each() kann man ja auch nehmen, wenn man wirklich alle Elemente braucht; da braucht man noch nicht einmal auf den nächsten Standard zu warten.

Gruß,
Simon2.
-
Hi ihr beiden,
ich meinte ja, ich benutze die Index-Schleife, wenn es mir auch auf den Index ankommt, d.h. ich schreibe lieber:
for ( size_t i = 0; i < keys.size(); i++) { cout << i << ":" << keys[i] << endl; }anstatt
for ( VInt::const_iterator it = keys.begin(); it != keys.end(); ++it ) { int i = it - keys.begin(); cout << i << ":" << *it << endl; }Außerdem finde ich die Iteratoren immer noch ziemlich fehleranfällig. Beim Index-Operator kriege ich wenigstens eine OutOfBounds-Exception. Beim Dereferenzieren auf einen falschen Iterator machts einfach peng! (außer man benutzt eine safe STL-Variante)
Dies soll jetzt nicht den Eindruck erwecken, daß ich die STL nicht zu schätzen weiß. Ohne sie kann ich nicht mehr leben -)
Aber wenn ich eine Anwender-Klasse entwickle, welche (konstanten) Zugriff auf eine interne Liste (z.B. vector) als Schnittstelle zur Verfügung stellt, dann stelle ich stets die Methoden
size_t GetCount() const; T Get(size_t index) const;zur Verfügung, anstatt wiederum einen Iterator nach außen zu geben.
Aber der eigentliche Inhalt dieses Threads hat ja die Frage nach der Geschwindigkeit gestellt und die haben wir wohl gegeben.
ENDE
-
Th schrieb:
Außerdem finde ich die Iteratoren immer noch ziemlich fehleranfällig. Beim Index-Operator kriege ich wenigstens eine OutOfBounds-Exception. Beim Dereferenzieren auf einen falschen Iterator machts einfach peng! (außer man benutzt eine safe STL-Variante)
Ich denke du solltest nochmal schleunigst alle deine Programme mit diesem Konstrukt durchgehen. Auf eine Exception vom Indexoperator wirst du nämlich lange warten ...
-
Normalerweise schon. Bei manchen Implementationen allerdings kann man BoundsChecking auch beim Indexoperator über defines einschalten. Dann fliegt eine Exception.
Bei der alten Rogue Wave mit _RWSTD_BOUNDS_CHECKING in der stdcomp.h
[edit]
Ich hab gerade noch ein wenig geschaut. Bei stlPort gibt es nichts dergleichen. Bei Dinkumware gibt es _HAS_ITERATOR_DEBUGGING was eine Fehlermeldung be Überschreiten der Arraygrenzen ausgibt.
[edit]
-
Hi Tc,
wie schon gesagt: Das Boundchecking ist das schlechteste Argument für die Verwendung des operator[]'s, weil der gerade so definiert ist, dass er das NICHT tut/n muss.
Wenn schon, dann solltest Du (wo vorhanden) at() verwenden.Ach ja:
Einint i = it - keys.begin();wäre mir zu teuer, wenn man mit einem simplen ++i ebensogut bedient ist.

Braunstein schrieb:
...Bei manchen Implementationen ...
Ist auch am sinnvollsten, Konstrukte zu verwenden, die möglichst nur auf einem bestimmten Compiler funktionieren !
V.A. mit #defines, bei denen niemand merkt, wenn der aktuelle Compiler (bzw. die aktuelle Version) nichts mit ihnen anfängt....:p
Gruß,
Simon2.
-
Simon2 schrieb:
wie schon gesagt: Das Boundchecking ist das schlechteste Argument für die Verwendung des operator[]'s, weil der gerade so definiert ist, dass er das NICHT tut/n muss.
AFAIK nicht tun darf. Der Indexoperator soll sich so verhalten wie der "echte" Indexoperator bei normalen Arrays.
-
.filmor schrieb:
Simon2 schrieb:
wie schon gesagt: Das Boundchecking ist das schlechteste Argument für die Verwendung des operator[]'s, weil der gerade so definiert ist, dass er das NICHT tut/n muss.
AFAIK nicht tun darf. Der Indexoperator soll sich so verhalten wie der "echte" Indexoperator bei normalen Arrays.
und wie verhält sich der "echte" indexoperator, wenn der index außerhalb der arraygrenzen liegt? genau: undefiniertes verhalten. das schließt die möglichkeit, dass eine exception geworfen wird, ein. es wird nur nicht garantiert, dass das passieren wird.
-
Sorry, daß ich soviel Diskussionen ausgelöst habe.
Ich hätte mein Beispiel mit dem []-Operator entsprechend meinem letzten Beispiel anpassen sollen.
Ich meinte, wenn ich nach außen eine Funktion Get() bzw. den Operator [] gebe, dann kann ich in meiner Implementierung selbst entscheiden, ob ich eine Bereichsüberprüfung checke, also intern z.B. vector<...>.at() benutze.
A propos, @simon2:
wenn man mit einem simplen ++i ebensogut bedient ist.
Dann sehe ich aber keinen Unterschied zu der Zähl-Schleife, wenn ich dann zusätzlich noch eine zweite Variable mitschleppe.
P.S.
Ich glaube, ich hatte gestern hier wohl einen Troll-Beitrag verfaßt -)