it-swarm.com.de

Benutzerdefinierter Iterator in C++

Ich habe eine Klasse TContainer, die aus mehreren stl-Auflistungszeigern auf die TItems-Klasse besteht.

Ich muss einen Iterator erstellen, um die Elemente in allen Sammlungen in meiner Klasse TContainer zu durchqueren, die den Client der inneren Arbeitsweise abstrahieren.

Was wäre ein guter Weg, dies zu tun? Sollte ich eine Klasse erstellen, die einen Iterator erweitert (wenn ja, welche Iteratorklasse sollte ich erweitern)? Sollte ich eine Iteratorklasse erstellen, die ein Aggregat von Iteratoren ist?

Ich brauche nur einen FORWARD_ONLY-Iterator.

I.E, Wenn dies mein Container ist: 

typedef std::vector <TItem*> ItemVector;
class TContainer {
   std::vector <ItemVector *> m_Items;
};

Was wäre ein guter Iterator, um alle Elemente zu durchlaufen, die in den Vektoren der Membervariable m_Items enthalten sind.

28
Sugar

Als ich vor einiger Zeit meinen eigenen Iterator durchführte, habe ich von std :: iterator geerbt und den Typ als ersten Parameter der Vorlage angegeben. Hoffentlich hilft das.

Für Forward-Iteratoren Benutzer forward_iterator_tag anstelle von input_iterator_tag im folgenden Code.

Diese Klasse wurde ursprünglich der istream_iterator-Klasse entnommen (und für meinen eigenen Gebrauch modifiziert, damit sie nicht mehr dem istram_iterator ähnelt).

template<typename T>
class <PLOP>_iterator
         :public std::iterator<std::input_iterator_tag,       // type of iterator
                               T,ptrdiff_t,const T*,const T&> // Info about iterator
{
    public:
        const T& operator*() const;
        const T* operator->() const;
        <PLOP>__iterator& operator++();
        <PLOP>__iterator operator++(int);
        bool equal(<PLOP>__iterator const& rhs) const;
};

template<typename T>
inline bool operator==(<PLOP>__iterator<T> const& lhs,<PLOP>__iterator<T> const& rhs)
{
    return lhs.equal(rhs);
}

Überprüfen Sie diese Dokumentation auf Iterator-Tags:
http://www.sgi.com/tech/stl/iterator_tags.html

Nachdem Sie die Informationen zu Iteratoren gerade noch einmal gelesen haben:
http://www.sgi.com/tech/stl/iterator_traits.html

Dies ist der alte Weg, Dinge zu tun (iterator_tags). Der modernere Ansatz besteht darin, iterator_traits <> für Ihren Iterator einzurichten, damit er vollständig mit der STL kompatibel ist.

31
Martin York

Wenn Sie Zugriff auf Boost haben, ist die Verwendung von iterator_facade die robusteste Lösung und ziemlich einfach zu bedienen.

22
James Hopkin

Lassen Sie uns zunächst ein wenig verallgemeinern:

typedef int value_type;
typedef std::vector<value_type*> inner_range;
typedef std::vector<inner_range*> outer_range;

Nun der Iterator:

struct my_iterator : std::iterator_traits<inner_range::iterator> 
{
    typedef std::forward_iterator_tag iterator_category;

    my_iterator(outer_range::iterator const & outer_iterator, 
                outer_range::iterator const & outer_end)
    : outer_iterator(outer_iterator), outer_end(outer_end)
    { 
        update();
    }

    my_iterator & operator++()
    {
        ++inner_iterator;
        if(inner_iterator == inner_end)
        {
            ++outer_iterator;
            update();
        }
        return *this;
    }

    reference operator*() const
    {   
        return *inner_iterator;
    }

    bool operator==(my_iterator const & rhs) const
    {   
        bool lhs_end = outer_iterator == outer_end;
        bool rhs_end = rhs.outer_iterator == rhs.outer_end;
        if(lhs_end && rhs_end)
            return true;
        if(lhs_end != rhs_end)
            return false;
        return outer_iterator == rhs.outer_iterator 
            && inner_iterator == rhs.inner_iterator;
    }

private:

    outer_range::iterator outer_iterator, outer_end;
    inner_range::iterator inner_iterator, inner_end;

    void update()
    {
        while(outer_iterator != outer_end)
        {
            inner_iterator = (*outer_iterator)->begin();
            inner_end = (*outer_iterator)->end();
            if(inner_iterator == inner_end)
                ++outer_iterator;
            else
                break;
        }
    }    
};

Diese Klasse geht davon aus, dass die äußeren Iteratoren Zeiger auf die inneren Bereiche enthalten, was in Ihrer Frage eine Voraussetzung war. Dies zeigt sich im Member update in den Pfeilen vor begin() und end(). Sie können diese Pfeile durch Punkte ersetzen, wenn Sie diese Klasse in Situationen verwenden möchten, in denen der äußere Iterator die inneren Bereiche nach Wert enthält. Beachten Sie übrigens, dass diese Klasse der Tatsache, dass der innere Bereich Zeiger enthält, nicht zugestimmt hat, dies müssen nur die Clients der Klasse wissen.

Der Code könnte kürzer sein, wenn wir boost::iterator_facade verwenden, aber es ist nicht notwendig, eine Boost-Abhängigkeit für etwas so einfaches hinzuzufügen. Außerdem sind die einzigen kniffligen Teile die Gleichheits- und Inkrementierungsoperationen, und diese müssen wir sowieso codieren.

Ich habe die folgenden Kesselplattenmitglieder als "Übungen für den Leser" hinterlassen:

  • postfix-Inkrement-Iterator
  • operator! =
  • standardkonstruktor
  • operator-> 

Eine weitere interessante Übung besteht darin, daraus eine Vorlage zu machen, die mit beliebigen Containern funktioniert. Der Code ist im Wesentlichen derselbe, mit der Ausnahme, dass Sie an einigen Stellen typename-Anmerkungen hinzufügen müssen. 

Anwendungsbeispiel:

int main()
{
    outer_type outer;
    int a = 0, b = 1, c = 2;
    inner_type inner1, inner2;
    inner1.Push_back(&a);
    inner1.Push_back(&b);
    inner2.Push_back(&c);
    outer.Push_back(&inner1);
    outer.Push_back(&inner2);

    my_iterator it(outer.begin(), outer.end());
                e(outer.end(), outer.end());
    for(; it != e; ++it)
        std::cout << **it << "\n";
}

Welche drucke:

0 1 2

18
Manuel

Ein Iterator ist nur eine Klasse, die eine bestimmte Schnittstelle unterstützt. Zumindest sollten Sie in der Lage sein:

  • erhöhen und/oder dekrementieren
  • dereferenzieren, um das Objekt zu erhalten, auf das es "zeigt"
  • teste es auf Gleichheit und Ungleichheit
  • kopieren und zuordnen

Sobald Sie eine Klasse haben, die dies sinnvoll für Ihre Sammlung tun kann, müssen Sie die Sammlung so ändern, dass sie Funktionen enthält, die Iteratoren zurückgeben. Zumindest wirst du wollen

  • eine begin () - Funktion, die eine Instanz Ihres neuen Iteratortyps zurückgibt, die sich am ersten Element befindet
  • eine end () - Funktion, die einen Iterator zurückgibt, der (möglicherweise absichtlich) an einem Punkt hinter dem Ende der Elemente in Ihrem Container positioniert ist
6
anon

Überprüfen Sie die Vorlagenbibliothek Views .

Besonders prüfen 

  1. Union View präsentiert zwei Container, die verkettet sind.
  2. Verkettungsansicht Darstellen einer Sammlung verketteter Container.
1
Nitin Bhide

Dies ist der einfachste Code, den ich produzieren konnte (für benutzerdefinierte Iteratoren). Beachten Sie, dass ich gerade erst anfange, diese Gegend zu erkunden. Dies ruft die integrierte upper_bound-Funktion auf, um die binäre Suche für eine Integer-Funktion auszuführen, x^2 als Beispiel.

#include <algorithm>
#include <iostream>

using namespace std;

class Iter
{
  public:
  int x;
  Iter() { x = -1; }
  Iter(int a) { x = a; }

  bool operator!=(Iter &i2) const { return x != i2.x; }
  void operator++() { x++; }
  void operator+=(int b) { x += b; }
  int operator-(const Iter &i2) const { return x - i2.x; }
  int operator*() const {
    cout << "calculating for x " << x << endl;
    return x*x;
  }

  typedef random_access_iterator_tag iterator_category;
  typedef int value_type;
  typedef int difference_type;
  typedef int* pointer;
  typedef int& reference;
};

main ()
{
  ios::sync_with_stdio(false);
  cout << upper_bound(Iter(0), Iter(100), 40).x << endl;
}

// :collapseFolds=1:folding=explicit:

Und so sieht die Ausgabe aus:

calculating for x 50
calculating for x 25
calculating for x 12
calculating for x 6
calculating for x 9
calculating for x 8
calculating for x 7
7
0
Jarekczek