Wie kann ich eine benutzerdefinierte Implementierung eines std-ähnlichen Iterators realisieren?

7

Ich habe eine sehr einfache Dateiverwaltungsdatenbank geschrieben, die im Prinzip so aussieht:

%Vor%

Nun würde ich gerne alle in der Datenbank enthaltenen Dateien durchlaufen wie mit std::iterator :

%Vor%

Ich habe Antworten auf ähnliche Fragen gelesen, einige schlagen vor, std::iterator abzuleiten, einige verwenden std::iterator_traits , aber ich verstehe nicht wirklich, wie das geht. Was kann möglicherweise schiefgehen, wenn Sie versuchen, einen benutzerdefinierten Iterator zu implementieren? Und was ist ein einfacher und doch eleganter Weg?

BEARBEITEN: Bitte denken Sie nicht an Boost, meine Frage ist eher konzeptueller Natur.

EDIT 2:

Die FileDB funktioniert so:

  • rootDir

    • foo1
      • bar1
        • foo1bar1_1.txt
        • foo1bar1_2.txt
      • bar2
        • foo1bar2_1.txt
        • foo1bar2_2.txt
    • foo2

    • fooN

      • barM

        • fooNBarM_x.txt

Im Grunde kann ich eine Datei mit ihrem Namen finden.

Da sich mein Container nicht im Speicher befindet, habe ich keine Zeiger auf seine Daten. Meine Idee war also, den Pfad der Datei im Iterator zu speichern. Auf diese Weise kann ich operator== mit einem String-Vergleich implementieren, da die Pfade eindeutig sein sollten. Der von fileDB.end() zurückgegebene Iterator wäre eine leere Zeichenfolge und operator* würde fileDB::loadFile() mit seinem Dateipfad aufrufen.

Meine größte Sorge ist über operator++ . Nachdem ich den Dateinamen gefunden habe, kann ich das enthaltende Verzeichnis finden und nach der nächsten Datei suchen, aber das ist wirklich ineffektiv. Irgendwelche Ideen, wie man das macht? Oder bin ich völlig falsch mit meinem ganzen Konzept?

    
Ben 30.03.2012, 14:57
quelle

3 Antworten

6
%Vor%     
D_E 30.03.2012, 17:10
quelle
11

Schreiben Sie Iteratoren selbst ist selten hübsch. Die beste Möglichkeit, Ihren Klassen einen Iterator hinzuzufügen, ist die Wiederverwendung eines vorhandenen Iterators. Sie sollten wissen, dass Zeiger zum Beispiel genauso gut sind wie Iteratoren in C ++, so dass es viele Möglichkeiten gibt, einen Iterator bereitzustellen, ohne dass Sie selbst einen eigenen schreiben müssen.

Dies ist im Grunde, wie C ++ auf viele Arten funktioniert. Es versucht, die Sprache für die Endbenutzer entbehrlich und einfach zu machen, indem es die Bibliotheksautoren stark belastet. I.e. Bibliotheksautoren können all die unpraktischen Dinge schreiben, also muss der Endbenutzer nicht. Iteratoren sind normalerweise ein Teil einer Bibliothek.

Nachdem das gesagt wurde, kommt hier der eigentliche hässliche Teil:

Um eigene Iteratoren schreiben zu können, müssen Sie Folgendes beachten:

Typmerkmale:

Typeigenschaften sind ein einfacher Mechanismus, um zusätzlichen Informationen zu Typen in C ++ hinzuzufügen, die sogar mit Typen funktionieren, die selbst nicht geändert werden können. Zum Beispiel ist es für einen Iterator wichtig zu wissen, worüber es iteriert (d. H. Den enthaltenen Typ). Der Weg, diese Information für einen gegebenen Iterator zu erhalten, hängt sehr vom Iterator ab. Für Iteratoren, bei denen es sich tatsächlich um Objekte handelt, können Sie typedefs in der Klasse hinzufügen und diese verwenden, aber für Iteratoren, bei denen es sich um Zeiger handelt, müssen Sie sie aus dem Zeigertyp ableiten. Um dies zu ermöglichen, wird die Information stattdessen in einem Typmerkmal gespeichert, so dass es einen einzigen Ort gibt, an dem ein Code diese Information sehen kann. Dies ist das Merkmal std::iterator_traits type.

std::iterator_traits arbeitet an allem, was sowohl von der std::iterator Vorlage als auch von jeder Art von Pointer abgeleitet ist, ohne irgendwelche Feinabstimmungen vorzunehmen. Oft ist es am besten, std::iterator als Basis zu verwenden, um zu vermeiden, dass Sie Ihre eigene Merkmalspezialisierung schreiben müssen. Für den Fall, dass Sie dies nicht tun können, ist es immer noch möglich, die notwendigen Eigenschaften bereitzustellen, aber es wird schwieriger sein.

Tag-Klassen und Iterator-Typen:

In C ++ gibt es verschiedene Arten von Iteratoren, die ein unterschiedliches Verhalten haben und viele verschiedene Dinge nicht können. Werfen Sie einen Blick auf Ссылка , um zu sehen, welche Art von Iteratoren verfügbar sind und was sie tun können. Die Diagramme sollen nicht in einer objektorientierten Weise (d. H. Ein input_iterator ist weder eine Unterklasse noch eine Basisklasse von forward_iterator ) gemeint sein, sondern eher als eine API-Art der Ableitung. I.e. Sie können alle Algorithmen verwenden, die für einen Eingabe-Iterator auch mit einem Vorwärts-Iterator geschrieben wurden. Die Tabelle auf der Seite wird Ihnen sagen, welche Methoden Sie für jede Kategorie bereitstellen müssen.

Da es sich bei diesen Kategorien nicht wirklich um Unterklassen handelt (dies sollte nicht der Fall sein, besonders wenn sie aus verschiedenen Arten von Sammlungen stammen), wird ein anderer Mechanismus verwendet, um die Fähigkeiten jedes Iterators zu identifizieren. Eine leere Tag-Klasse ist auch in std::iterator_traits enthalten und beschreibt jeden Iterator, der angibt, was dieser Iterator kann und was nicht. Wenn Sie keine eigenen Merkmale schreiben, müssen Sie diese Tag-Klasse bei der Instanziierung der Vorlage std::iterator zur Verfügung stellen.

Beispiel:

Dieses Beispiel stammt aus dem Abschnitt cplusplus.com über Iteratoren:

%Vor%

Dieser Iterator ist nicht wirklich sinnvoll, da er nur einen Zeiger umschließt, der auch direkt verwendet werden könnte. Es kann jedoch als Erklärung dienen. Der Iterator wird von std::iterator als input_iterator abgeleitet, indem das entsprechende Tag angegeben wird. Außerdem wird der Vorlage gesagt, dass dieser Iterator über int s iteriert. Alle anderen Typen, die difference_type , reference , poiner usw. benötigen, werden automatisch von der Vorlage berücksichtigt. In manchen Fällen kann es sinnvoll sein, einige dieser Typen manuell zu ändern (zum Beispiel muss std::shared_ptr manchmal als pointer verwendet werden). Auch die Eigenschaften, die für diesen Iterator benötigt werden, existieren automatisch, da sie bereits von std::iterator abgeleitet sind und die std::iterator_traits wissen, wo sie alle notwendigen Informationen finden können.

    
LiKao 30.03.2012 16:42
quelle
3

Hier ist der Iterator, der Unterknoten während der Traversierung berechnet. Ich habe es für Windows geschrieben, aber ich denke, es ist nicht schwer, es für andere Plattformen zu castomisieren.

%Vor%     
D_E 30.03.2012 23:51
quelle

Tags und Links