Eigene Iteratoren erstellen

141

Ich versuche, C ++ zu lernen. Verzeihen Sie mir, wenn diese Frage einen Mangel an Grundkenntnissen aufweist. Sie sehen, Tatsache ist, dass ich einen Mangel an Grundkenntnissen habe.

Ich möchte Hilfe beim Erstellen eines Iterators für eine von mir erstellte Klasse.

Ich habe eine Klasse 'Form', die einen Container mit Punkten enthält. Ich habe eine Klasse 'Stück', die auf eine Form verweist und eine Position für die Form definiert. Stück hat keine Form, es verweist nur auf eine Form.

Ich möchte, dass es so aussieht, als wäre Piece ein Container mit Punkten, die mit denen der Form identisch sind, auf die es verweist, wobei jedoch der Versatz der Position des Pieces hinzugefügt wird.

Ich möchte in der Lage sein, die Punkte des Stücks so zu durchlaufen, als wäre das Stück selbst ein Container. Ich habe ein wenig herumgelesen und nichts gefunden, was mir geholfen hat. Für Hinweise wäre ich sehr dankbar.

Howard May
quelle
6
Wenn Sie einen Beispielcode veröffentlichen, können Sie besser beschreiben, was Sie besser können als nur einfachen englischen Text.
Greg Rogers
3
Das Erstellen von benutzerdefinierten Iteratoren ist wahrscheinlich keine grundlegende Aufgabe, zumindest keine Zwischenstufe.
ldog

Antworten:

41

Sie sollten Boost.Iterators verwenden. Es enthält eine Reihe von Vorlagen und Konzepten zum Implementieren neuer Iteratoren und Adapter für vorhandene Iteratoren. Ich habe einen Artikel zu diesem Thema geschrieben . Es ist im Dezember 2008 ACCU Magazin. Es wird eine (IMO) elegante Lösung für genau Ihr Problem erläutert: Anzeigen von Mitgliedssammlungen aus einem Objekt mithilfe von Boost.Iterators.

Wenn Sie nur die STL verwenden möchten, enthält das Josuttis-Buch ein Kapitel zur Implementierung Ihrer eigenen STL-Iteratoren.

Roel
quelle
3
Nur eine kleine Bemerkung: Das Buch spricht über die C ++ Standard Library, nicht über die STL - diese sind unterschiedlich, werden aber sehr verwirrt (ich bin / war auch schuldig)
CppChris
62

/ EDIT: Ich sehe, hier ist tatsächlich ein eigener Iterator erforderlich (ich habe die Frage zuerst falsch verstanden). Trotzdem lasse ich den folgenden Code stehen, da er unter ähnlichen Umständen nützlich sein kann.


Ist hier eigentlich ein eigener Iterator notwendig? Vielleicht reicht es aus, alle erforderlichen Definitionen an den Container mit den tatsächlichen Punkten weiterzuleiten:

// Your class `Piece`
class Piece {
private:
    Shape m_shape;

public:

    typedef std::vector<Point>::iterator iterator;
    typedef std::vector<Point>::const_iterator const_iterator;

    iterator begin() { return m_shape.container.begin(); }

    const_iterator begin() const { return m_shape.container.begin(); }

    iterator end() { return m_shape.container.end(); }

    const_iterator end() const { return m_shape.const_container.end(); }
}

Dies setzt voraus, dass Sie einen vectorinternen verwenden, der Typ kann jedoch leicht angepasst werden.

Konrad Rudolph
quelle
Vielleicht möchte er den STL-Algorithmus oder funktionale Merkmale gegen seine Klasse verwenden ...
gbjbaanb
2
Die ursprüngliche Frage besagt tatsächlich, dass der Iterator des Stückcontainers die Werte bei der Rückgabe ändern sollte. Dies würde einen separaten Iterator erfordern, obwohl er wahrscheinlich größtenteils vom Original geerbt oder auf andere Weise erhalten werden sollte.
workmad3
@gbjbaanb: Die gute Sache über meinen Code ist , dass es kann von STL - Algorithmen verwendet werden.
Konrad Rudolph
1
Einige Jahre später und dies gehört immer noch zu den Top-Ergebnissen bei Google ... Es ist jetzt möglich, dies zu verallgemeinern, indem Sie so etwas tun:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533
20

Hier ist das Entwerfen eines STL-ähnlichen benutzerdefinierten Containers ein ausgezeichneter Artikel, in dem einige der grundlegenden Konzepte erläutert werden, wie eine STL-ähnliche Containerklasse zusammen mit der Iterator-Klasse dafür entworfen werden kann. Reverse Iterator (etwas härter) bleibt allerdings als Übung :-)

HTH,

Abhay
quelle
15

Sie können diesen DJJ-Artikel lesen

Erben Sie grundsätzlich von std :: iterator, um den größten Teil der Arbeit für Sie zu erledigen.

gbjbaanb
quelle
2
Beachten Sie, dass dies in C ++ 17 std::iteratorals veraltet markiert ist .
Mandrake
2

Das Schreiben von benutzerdefinierten Iteratoren in C ++ kann sehr ausführlich und komplex sein.

Da ich keine minimale Möglichkeit zum Schreiben eines benutzerdefinierten Iterators finden konnte, habe ich diesen Vorlagenheader geschrieben , der möglicherweise hilfreich ist. Um die PieceKlasse beispielsweise iterierbar zu machen :

#include <iostream>
#include <vector>

#include "iterator_tpl.h"

struct Point {
  int x;
  int y;
  Point() {}
  Point(int x, int y) : x(x), y(y) {}
  Point operator+(Point other) const {
    other.x += x;
    other.y += y;
    return other;
  }
};

struct Shape {
  std::vector<Point> vec;
};

struct Piece {
  Shape& shape;
  Point offset;
  Piece(Shape& shape, int x, int y) : shape(shape), offset(x,y) {}

  struct it_state {
    int pos;
    inline void next(const Piece* ref) { ++pos; }
    inline void begin(const Piece* ref) { pos = 0; }
    inline void end(const Piece* ref) { pos = ref->shape.vec.size(); }
    inline Point get(Piece* ref) { return ref->offset + ref->shape.vec[pos]; }
    inline bool cmp(const it_state& s) const { return pos != s.pos; }
  };
  SETUP_ITERATORS(Piece, Point, it_state);
};

Dann können Sie es als normalen STL-Container verwenden:

int main() {
  Shape shape;
  shape.vec.emplace_back(1,2);
  shape.vec.emplace_back(2,3);
  shape.vec.emplace_back(3,4);

  Piece piece(shape, 1, 1);

  for (Point p : piece) {
    std::cout << p.x << " " << p.y << std::endl;
    // Output:
    // 2 3
    // 3 4
    // 4 5
  }

  return 0;
}

Es ermöglicht auch das Hinzufügen anderer Arten von Iteratoren wie const_iteratoroder reverse_const_iterator.

Ich hoffe, es hilft.

VinGarcia
quelle
1

Die Lösung für Ihr Problem ist nicht die Erstellung eigener Iteratoren, sondern die Verwendung vorhandener STL-Container und Iteratoren. Speichern Sie die Punkte in jeder Form in einem containerähnlichen Vektor.

class Shape {
    private:
    vector <Point> points;

Was Sie von da an tun, hängt von Ihrem Design ab. Der beste Ansatz besteht darin, Punkte in Methoden innerhalb von Shape zu durchlaufen.

for (vector <Point>::iterator i = points.begin(); i != points.end(); ++i)
    /* ... */

Wenn Sie auf Punkte außerhalb von Shape zugreifen müssen (dies kann ein Zeichen für ein fehlerhaftes Design sein), können Sie in Shape-Methoden erstellen, die die Iteratorzugriffsfunktionen für Punkte zurückgeben (in diesem Fall erstellen Sie auch ein öffentliches Typedef für den Punktecontainer). Schauen Sie sich die Antwort von Konrad Rudolph an, um Einzelheiten zu diesem Ansatz zu erfahren.

Diomidis Spinellis
quelle
3
Er muss immer noch seinen eigenen Iterator erstellen, der Anfragen an Piece an die Formen weiterleitet, die sich in diesem Stück befinden. Benutzerdefinierte Iteratoren sind hier ein großartiges Werkzeug und sehr elegant zu verwenden.
Roel