Ich habe eine Sammlung erstellt, für die ich einen STL-artigen Iterator mit wahlfreiem Zugriff bereitstellen möchte. Ich habe nach einer Beispielimplementierung eines Iterators gesucht, aber keine gefunden. Ich weiß um die Notwendigkeit von Konstantenüberladungen []
und *
Operatoren. Was sind die Anforderungen an einen Iterator im "STL-Stil" und welche anderen Fallstricke sind zu vermeiden (falls vorhanden)?
Zusätzlicher Kontext: Dies ist für eine Bibliothek und ich möchte keine Abhängigkeit davon einführen, es sei denn, ich muss es wirklich. Ich schreibe meine eigene Sammlung, um die Binärkompatibilität zwischen C ++ 03 und C ++ 11 mit demselben Compiler zu gewährleisten (also keine STL, die wahrscheinlich kaputt gehen würde).
c++
iterator
const-iterator
Tamás Szelei
quelle
quelle
Antworten:
http://www.cplusplus.com/reference/std/iterator/ verfügt über ein praktisches Diagramm, in dem die Spezifikationen von § 24.2.2 des C ++ 11-Standards aufgeführt sind. Grundsätzlich haben die Iteratoren Tags, die die gültigen Operationen beschreiben, und die Tags haben eine Hierarchie. Das Folgende ist rein symbolisch, diese Klassen existieren eigentlich nicht als solche.
Sie können sich entweder spezialisieren
std::iterator_traits<youriterator>
oder dieselben Typedefs in den Iterator selbst einfügen oder vonstd::iterator
(der diese Typedefs hat) erben . Ich bevorzuge die zweite Option, um Änderungen imstd
Namespace zu vermeiden und um die Lesbarkeit zu gewährleisten, aber die meisten Menschen erben vonstd::iterator
.Beachten Sie die iterator_category sollte eine sein
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
, oderstd::random_access_iterator_tag
, je nachdem , welche Anforderungen Ihre Iterator erfüllt. Je nach Ihrem Iterator können Sie wählen , sich zu spezialisierenstd::next
,std::prev
,std::advance
, undstd::distance
auch, aber dies wird selten benötigt. In äußerst seltenen Fällen möchten Sie sich vielleicht spezialisierenstd::begin
undstd::end
.Ihr Container sollte wahrscheinlich auch einen haben
const_iterator
, der ein (möglicherweise veränderbarer) Iterator für konstante Daten ist, der Ihrem ähnlich ist,iterator
außer dass er implizit aus a konstruierbar seiniterator
sollte und Benutzer die Daten nicht ändern können sollten. Es ist üblich, dass sein interner Zeiger ein Zeiger auf nicht konstante Daten ist und von dieseniterator
geerbt wurdeconst_iterator
, um die Codeduplizierung zu minimieren.Mein Beitrag beim Schreiben Ihres eigenen STL-Containers enthält einen vollständigeren Container / Iterator-Prototyp.
quelle
std::iterator_traits
Sie können die Typedefs nicht nur spezialisieren oder selbst definieren, sondern auch einfach ableitenstd::iterator
, wodurch diese für Sie abhängig von den Vorlagenparametern definiert werden.const_iterator
. Was fehlte meinem Beitrag noch? Sie scheinen zu implizieren, dass der Klasse noch mehr hinzugefügt werden muss, aber die Frage bezieht sich speziell auf die Implementierung von Iteratoren.std::iterator
wurde vorgeschlagen, in C ++ 17 veraltet zu sein ; es war nicht so, aber ich würde mich nicht darauf verlassen, dass es noch viel länger da ist.std::iterator
war doch veraltet.operator bool
ist unglaublich gefährlich. Jemand wird versuchen, damit das Ende eines Bereichs zu erkennenwhile(it++)
, aber alles, was wirklich überprüft wird, ist, ob der Iterator mit einem Parameter erstellt wurde.Die iterator_facade- Dokumentation von Boost.Iterator bietet ein nettes Tutorial zum Implementieren von Iteratoren für eine verknüpfte Liste. Könnten Sie dies als Ausgangspunkt für die Erstellung eines Iterators mit wahlfreiem Zugriff über Ihrem Container verwenden?
Wenn nichts anderes, können Sie sich die von
iterator_facade
und bereitgestellten Elementfunktionen und Typedefs ansehen und sie als Ausgangspunkt für die Erstellung Ihrer eigenen verwenden.quelle
Thomas Becker schrieb einen nützlichen Artikel über das Thema hier .
Es gab auch diesen (vielleicht einfacheren) Ansatz, der zuvor in SO vorgestellt wurde: Wie werden benutzerdefinierte Iteratoren und const_iterators korrekt implementiert?
quelle
Hier ist ein Beispiel eines Rohzeiger-Iterators.
Sie sollten keine Iteratorklasse verwenden, um mit rohen Zeigern zu arbeiten!
Problemumgehung auf Basis des Raw-Zeigerbereichs. Bitte korrigieren Sie mich, wenn es einen besseren Weg gibt, eine bereichsbasierte Schleife aus dem Rohzeiger zu erstellen.
Und einfacher Test
quelle
Zunächst können Sie hier eine Liste der verschiedenen Operationen suchen , die die einzelnen Iteratortypen unterstützen müssen.
Wenn Sie Ihre Iterator-Klasse erstellt haben, müssen Sie sich entweder darauf spezialisieren
std::iterator_traits
und einige erforderlichetypedef
s (wieiterator_category
odervalue_type
) bereitstellen oder sie alternativ ableitenstd::iterator
, wodurch dietypedef
für Sie erforderlichen s definiert werden und daher mit der Standardeinstellung verwendet werden könnenstd::iterator_traits
.Haftungsausschluss: Ich weiß, dass einige Leute nicht so
cplusplus.com
sehr mögen , aber sie liefern einige wirklich nützliche Informationen dazu.quelle
Ich war / bin aus verschiedenen Gründen im selben Boot wie Sie (teils lehrreich, teils eingeschränkt). Ich musste alle Container der Standardbibliothek neu schreiben und die Container mussten dem Standard entsprechen. Das heißt, wenn ich meinen Container gegen die stl- Version austausche , funktioniert der Code genauso. Was auch bedeutete, dass ich die Iteratoren neu schreiben musste.
Wie auch immer, ich sah EASTL an . Abgesehen davon, dass ich eine Menge über Container gelernt habe, die ich die ganze Zeit mit den stl- Containern oder durch meine Grundstudiengänge nie gelernt habe. Der Hauptgrund ist, dass EASTL besser lesbar ist als das stl- Gegenstück (ich fand, dass dies einfach auf das Fehlen aller Makros und den einfachen Codierungsstil zurückzuführen ist). Es gibt einige eklige Dinge (wie #ifdefs für Ausnahmen), aber nichts, was Sie überwältigen könnte.
Wie bereits erwähnt, lesen Sie die Referenz von cplusplus.com zu Iteratoren und Containern.
quelle
Ich habe versucht, das Problem zu lösen, dass mehrere verschiedene Textarrays durchlaufen werden können, die alle in einer großen speicherresidenten Datenbank gespeichert sind
struct
.Das Folgende wurde mit Visual Studio 2017 Community Edition in einer MFC-Testanwendung erarbeitet. Ich füge dies als Beispiel hinzu, da dieses Posting eines von mehreren war, auf die ich gestoßen bin und die Hilfe lieferten, die aber für meine Bedürfnisse immer noch nicht ausreichten.
Die
struct
Daten, die die speicherresidenten Daten enthielten, sahen ungefähr so aus. Ich habe die meisten Elemente der Kürze halber entfernt und auch die verwendeten Präprozessordefinitionen nicht berücksichtigt (das verwendete SDK ist sowohl für C als auch für C ++ und alt).Ich war daran interessiert, Iteratoren für die verschiedenen
WCHAR
zweidimensionalen Arrays zu haben, die Textzeichenfolgen für die Mnemonik enthielten.Der aktuelle Ansatz besteht darin, eine Vorlage zu verwenden, um eine Proxy-Klasse für jedes der Arrays zu definieren und dann eine einzelne Iterator-Klasse zu haben, die zum Iterieren über ein bestimmtes Array verwendet werden kann, indem ein Proxy-Objekt verwendet wird, das das Array darstellt.
Eine Kopie der speicherresidenten Daten wird in einem Objekt gespeichert, das das Lesen und Schreiben der speicherresidenten Daten von / auf die Festplatte übernimmt. Diese Klasse
CFilePara
enthält die Proxy-Klasse mit Vorlagen (MnemonicIteratorDimSize
und die Unterklasse, von der sie abgeleitet istMnemonicIteratorDimSizeBase
) und die Iterator-KlasseMnemonicIterator
.Das erstellte Proxy-Objekt wird an ein Iterator-Objekt angehängt, das über eine von einer Basisklasse beschriebene Schnittstelle, von der alle Proxy-Klassen abgeleitet sind, auf die erforderlichen Informationen zugreift. Das Ergebnis ist ein einzelner Typ von Iteratorklasse, der mit mehreren verschiedenen Proxy-Klassen verwendet werden kann, da die verschiedenen Proxy-Klassen alle dieselbe Schnittstelle, die Schnittstelle der Proxy-Basisklasse, verfügbar machen.
Das erste war, eine Reihe von Bezeichnern zu erstellen, die einer Klassenfactory zur Verfügung gestellt wurden, um das spezifische Proxy-Objekt für diesen Mnemoniktyp zu generieren. Diese Kennungen werden als Teil der Benutzeroberfläche verwendet, um die bestimmten Bereitstellungsdaten zu identifizieren, die der Benutzer sehen und möglicherweise ändern möchte.
Die Proxy-Klasse
Die Proxy-Klasse mit Vorlagen und ihre Basisklasse lauten wie folgt. Ich musste verschiedene Arten von
wchar_t
Text-String-Arrays unterbringen . Die zweidimensionalen Arrays hatten je nach Art (Zweck) der Mnemonik unterschiedliche Anzahlen von Mnemoniken, und die verschiedenen Arten von Mnemoniken hatten unterschiedliche maximale Längen und variierten zwischen fünf Textzeichen und zwanzig Textzeichen. Vorlagen für die abgeleitete Proxy-Klasse passten natürlich zu der Vorlage, die die maximale Anzahl von Zeichen in jeder Mnemonik erfordert. Nachdem das Proxy-Objekt erstellt wurde, verwenden wir dieSetRange()
Methode, um das tatsächliche Mnemonik-Array und seinen Bereich anzugeben.Die Iterator-Klasse
Die Iteratorklasse selbst lautet wie folgt. Diese Klasse bietet nur grundlegende Forward-Iterator-Funktionen, die derzeit nur benötigt werden. Ich gehe jedoch davon aus, dass sich dies ändern oder erweitern wird, wenn ich etwas Zusätzliches benötige.
Die Proxy-Objekt-Factory bestimmt anhand der Mnemonik-ID, welches Objekt erstellt werden soll. Das Proxy-Objekt wird erstellt und der zurückgegebene Zeiger ist der Standard-Basisklassentyp, um eine einheitliche Schnittstelle zu erhalten, unabhängig davon, auf welche der verschiedenen Mnemonikabschnitte zugegriffen wird. Die
SetRange()
Methode wird verwendet, um dem Proxy-Objekt die spezifischen Array-Elemente anzugeben, die der Proxy darstellt, und den Bereich der Array-Elemente.Verwenden der Proxy-Klasse und des Iterators
Die Proxy-Klasse und ihr Iterator werden wie in der folgenden Schleife gezeigt verwendet, um ein
CListCtrl
Objekt mit einer Liste von Mnemonics zu füllen . Ich benutze es,std::unique_ptr
damitstd::unique_ptr
der Speicher bereinigt wird , wenn die Proxy-Klasse, die ich nicht mehr benötige und die nicht mehr funktioniert.Mit diesem Quellcode wird ein Proxy-Objekt für das Array erstellt, das
struct
dem angegebenen Mnemonik-Bezeichner entspricht. Anschließend wird ein Iterator für dieses Objekt erstellt, ein Bereichfor
zum Ausfüllen desCListCtrl
Steuerelements verwendet und anschließend bereinigt. Dies sind alleswchar_t
Rohtextzeichenfolgen, die genau der Anzahl der Array-Elemente entsprechen können. Daher kopieren wir die Zeichenfolge in einen temporären Puffer, um sicherzustellen, dass der Text mit Null abgeschlossen ist.quelle
Und jetzt ein Schlüsseliterator für die bereichsbasierte for-Schleife.
Verwendungszweck:
Das habe ich gesucht. Aber niemand hatte es, wie es scheint.
Sie erhalten meine OCD-Code-Ausrichtung als Bonus.
Schreiben Sie als Übung Ihre eigenen für
values(my_map)
quelle