Eigene Iteratoren schreiben
-
Für folgenes Beispiel möchte ich eigene Iteratoren schreiben:
template<class T> class myContainer { public: myContainer(std::vector<T>& v, const std::vector<unsigned>& indexVector) : vec(v), indices(indexVector) { } /* ... */ T get(unsigned index) { return vec[ indeices[index] ]; } void set(unsigned index, T value) { vec[ indices[index] ] = value; } private: std::vector<T>& vec; std::vector<unsigned> indices; };
myContainer ist eine Klasse, die mir den Zugriff auf einen Vektor verwaltet - mich interessieren nur bestimmte Elemente des Vektors. Wie implementiere ich für so eine Klasse sinnvolle Iteratoren, so dass z.b. folgender Code funktioniert:
std::vector<int> vec; std::vector<unsigned> ind; for(int i = 0; i < 10; ++i) { vec.push_back(i); if(i%2) ind.push_back(i); } myContainer<int> cont(vec,ind); /* iterator_type */ it; for(it = cont.begin(); it != cont.end(); ++it) { std::cout << *it; *it = 0; } std::cout << std::endl; vector<int>::iterator it2; for(it2 = vec.begin(); it2 != vec.end(); ++it) { std::cout << *it2; }
Erhoffte Ausgabe:
02468
0103050709Soweit ich es verstanden habe, müsste ich am besten einen iterator und einen const_iterator implementieren, die jeweils eine Referenz auf myContainer beinhalten und einen Index. Zusätzlich werden die Operatoren * und (zweimal?) ++ überladen. Würde man solche Iteratoren von "Standard-Iteratoren" ableiten? Überläd man auch den op->?
-
Ich habe noch nicht ganz so verstanden, was dein eigener Container macht, aber grundsätzlich hier mal die Anforderungen an die Iteratoren der verschiedenen Kategorien:
http://www.cplusplus.com/reference/std/iterator/Dann noch ein paar Tipps zu den Operatoren:
Überladung von Operatoren (Teil 1)
Überladung von Operatoren (Teil 2)
Überladung von Operatoren (Teil 3)Sehr hilfreich sind dabei die Teile 2 und 3, welche die Boost.Operators behandeln, die einem eine Menge Tipparbeit ersparen können.
Übrigens, zum Teil reicht auch einfach ein
typedef
auf einen Iterator eines internen Containers oder ein Wrapper um denselben. Kommt halt wirklich darauf an, was der Iterator können soll.Grüssli
-
Schon mal danke für die Tipps.
Dravere schrieb:
Ich habe noch nicht ganz so verstanden, was dein eigener Container macht,
Er soll den Zugriff auf einen std::vector verwalten: Mich interessieren (manchmal) nur bestimmte Elemente des vectors, mit denen ich dann Operationen durchführen will (z.b. nur die geraden Elemente, nur die ersten 5 Elemente, 7 Elemente unregelmäßig über den ganzen Vektor verteilt....). Daher speichere ich einn weiteren vektor mit den Indices der Elemente, die mich interessieren. Mit dem eigenen "Container" möchte ich den Zugriff auf die interessanten Elemente erleichtern.
Dravere schrieb:
Übrigens, zum Teil reicht auch einfach ein
typedef
auf einen Iterator eines internen Containers oder ein Wrapper um denselben. Kommt halt wirklich darauf an, was der Iterator können soll.Das waren die Hinweise, die ich mir erhofft habe. Kannst du mir hier Tipps geben, was in meinem Fall hilfreich werden könnte?
-
Also, wenn ich das richtig verstanden habe, dann soll beim Zugriff auf den Iterator immer folgendes Element zurückgegeben werden?
vec[indices[x]]
Grundsätzlich so, wie es deine
set
undget
Methoden machen? Wenn dies der Fall ist, dann hast du Fehler im Beispielcode, was mich eben wohl verwirrt hatDann ist es aber relativ einfach. Grundsätzlich entspricht es genau der Iteration über den
std::vector<unsigned> indices
(übrigens, verwende vielleicht lieberstd::vector<typename std::vector<T>::size_type> indices
oder sowas ähnliches *Sicherheitsfanatiker* :)).
Du willst allerdings bei der Iteration den Zugriff auf einen anderen Container umleiten. Also Wrappen und dem Wrapperobjekt denstd::vector<T> vec
als Referenz oder besser Zeiger mitgeben.Beispielcode nur als Anregung:
class MyIterator { private: std::vector<typename std::vector<T>::size_type>::iterator m_iter; std::vector<T>* m_vec; // Die langen Namen hier nur zur Verdeutlichung. Kann man natürlich vereinfachen. // ... public: T& operator *() { return (*m_vec)[*m_iter]; } // ... MyIterator operator ++() { ++m_iter; return *this; } };
Grüssli
-
Taurin schrieb:
Erhoffte Ausgabe:
02468
0103050709Ok.
Taurin schrieb:
Soweit ich es verstanden habe, müsste ich am besten einen iterator und einen const_iterator implementieren, die jeweils eine Referenz auf myContainer beinhalten und einen Index. Zusätzlich werden die Operatoren * und (zweimal?) ++ überladen. Würde man solche Iteratoren von "Standard-Iteratoren" ableiten? Überläd man auch den op->?
Je nachdem, was es für ein Iterator werden soll (Es gibt unterschiedliche verfeinerte Konzepte). Hier bietet sich an, ein Iterator-Klassen-Template zu schreiben, welches beide Iteratoren (den von dem "backend"-Container und dem, von dem Index-Container). Damit sparst Du Dir auch zwei Versionen für const und nicht-const:
// RAIter: RandomAccessIterator für die Werte // IndexIter: Ein Iterator für die Indizes template<typename RAIter, typename IndexIter> class indexed_iter { public: typedef typename std::iterator_traits<RAIter>::value_type value_type; typedef typename std::iterator_traits<RAIter>::pointer pointer; typedef typename std::iterator_traits<RAIter>::reference reference; typedef typename std::iterator_traits<IndexIter>::iterator_category iterator_category; typedef typename std::iterator_traits<IndexIter>::difference_type difference_type; indexed_iter(RAIter backend, IndexIter idxiter) : backend(backend), idxiter(idxiter) {} reference operator*() const {return backend[*idxiter];} indexed_iter& operator++() {++idxiter;} ..... private: RAIter backend; IndexIter idxiter; };
(Code nicht getestet)
Was ein RandomAccessIterator noch so können muss, kann man nachlesen. Ich sehe gerade, Dravere ist mir zuvor gekommen. Ich stimme ihm zu, was unsigned als Index angeht. Man sollte auch die 5 typedefs (oder eine Spezialisierung von std::iterator_traits) auch nicht vergessen.
Gruß,
SP