Iterieren über eigene Container
Wie kann die InfiniteSequence
vom letzten Mal etwas konformer zur Standardbibliothek gemacht werden? Ganze einfach: man
verpasst ihr passende Iteratoren, wie sie für die anderen Container auch vorgegeben sind. Der Einfachheit halber sind das
hier mal nur ForwardIterator
. Vom Entwurf des Containers her wären aber auch Bidirektional
oder RandomAccess
möglich.
Video
Code
|
|
Erklärung
Gegenüber der ursprünglichen InfiniteSequence
wurde diese hier um zwei größere Sachen verändert: zum einen ist die Methode
get()
aus der öffentlichen Schnittstelle entfallen und dient nun stattdessen mit einem zusätzlichen Parameter dem
wahlfreien Zugriff auf Elemente der Sequenz. Zum anderen gibt es eine nested class namens const_iterator
, die den
Iteratortyp der InfiniteSequence
repräsentiert. const_iterator
erfüllt die Anforderungen an einen ForwardIterator
: es
gibt einen Default- und Copy-Konstruktor, einen Copy-Assignment-Operator, Objekte des Iteratortyps können auf Gleichheit
oder Ungleichheit verglichen werden (der operator!=
fehlt übrigens in der Videoversion. Das ist ein Fehler, der so an sich
erstmal nicht auffällt, da ich ihn nicht verwende), es gibt ein Prä- und Postfixinkrement und eine Dereferenzierung. Da das
ganze ein konstanter Iterator ist (die zugrundliegende Sequenz ist ja auch nicht veränderlich) liefert operator*
eine
konstante Referenz auf den Wertetyp zurück. Soweit alles wie zu erwarten.
const_iterator
als nested class zu realisieren hat einen Vorteil: die Klasse selbst muss kein Template sein. Sie ist als
abhängiger Typ von InfiniteSequence
automatisch mit parametrisiert. InfiniteSequence<int>::const_iterator
und
InfiniteSequence<double>::const_iterator
sind also bereits unterschiedliche Typen. Ein extra Templateparameter für den
Iterator ist unnötig.
Kleine Besonderheiten im Quelltext: zum einen die beiden Delegate Constructors in Zeile 21 und 30. Mit C++11 kommt die
Möglichkeit hinzu, Konstruktoren der eigenen Klasse aus anderen Konstruktoren aufzurufen. Das ist immer dann ganz angenehm,
wenn man gewisse Teile einer einheitlichen Initialisierung machen und dann in einigen Konstruktoren noch zusätzliche
Aktionen durchführen möchte. In C++03 bedeutete das immer, dass man eine extra init
-Funktion mit der einheitlichen
Initialisierung implementieren musste, die jeder Konstruktor aufrufen konnte (und damit lassen sich einige Randfälle
speziell bei der Initialisierung der Membervariablen der Klasse nicht abdecken). C++11 bietet hier jetzt die doch etwas
bequemere Variante mit der Konstruktorweiterleitung. Dazu steht der entsprechende Aufruf des anderen Konstruktors einfach in
der Initialisierung des aufrufenden Konstruktors. Hier im Beispiel wäre das nicht zwingend notwendig. Man könnte das
Verhalten hier genauso mit einem Konstruktor und entsprechenden Default-Werten für die Parameter lösen. In anderen Fällen
ist das nicht so einfach.
Als weiteres Beispiel ist im Quelltext die Implementierung des Postfixinkrements zu sehen. Der Unterschied zum
Präfixinkrement: der Zustand des Iterators vor der Inkrementierung wird zurückgeliefert (im Beispiel vorher in der Variable
copy
gespeichert).