Ich hatte das Gefühl, dass keine der Antworten hier erklärt, warum ich Iteratoren als allgemeines Konzept für die Indizierung in Containern mag. Beachten Sie, dass der größte Teil meiner Erfahrung mit Iteratoren nicht aus C ++ stammt, sondern aus übergeordneten Programmiersprachen wie Python.
Die Iterator-Schnittstelle stellt weniger Anforderungen an die Verbraucher Ihrer Funktion, sodass die Verbraucher mehr damit anfangen können.
Wenn Sie nur in der Lage sein müssen, vorwärts zu iterieren, kann der Entwickler nicht nur indizierbare Container verwenden, sondern auch jede Klasse, die implementiert operator++(T&)
, operator*(T)
und operator!=(const &T, const &T)
.
#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
for (auto current = begin; current != end; ++current) {
std::cout << *current << "\n";
}
}
// elsewhere...
printAll(myVector.begin(), myVector.end());
Ihr Algorithmus funktioniert für den Fall, dass Sie ihn benötigen - er iteriert über einen Vektor -, kann aber auch für Anwendungen nützlich sein, die Sie nicht unbedingt erwarten:
#include <random>
class RandomIterator
{
private:
std::mt19937 random;
std::uint_fast32_t current;
std::uint_fast32_t floor;
std::uint_fast32_t ceil;
public:
RandomIterator(
std::uint_fast32_t floor = 0,
std::uint_fast32_t ceil = UINT_FAST32_MAX,
std::uint_fast32_t seed = std::mt19937::default_seed
) :
floor(floor),
ceil(ceil)
{
random.seed(seed);
++(*this);
}
RandomIterator& operator++()
{
current = floor + (random() % (ceil - floor));
}
std::uint_fast32_t operator*() const
{
return current;
}
bool operator!=(const RandomIterator &that) const
{
return current != that.current;
}
};
int main()
{
// roll a 1d6 until we get a 6 and print the results
RandomIterator firstRandom(1, 7, std::random_device()());
RandomIterator secondRandom(6, 7);
printAll(firstRandom, secondRandom);
return 0;
}
Der Versuch, einen Operator in eckigen Klammern zu implementieren, der etwas Ähnliches wie dieser Iterator ausführt, wäre erfunden, während die Iteratorimplementierung relativ einfach ist. Der Operator in eckigen Klammern hat auch Auswirkungen auf die Funktionen Ihrer Klasse - die Sie auf einen beliebigen Punkt indizieren können -, deren Implementierung möglicherweise schwierig oder ineffizient ist.
Iteratoren eignen sich auch zur Dekoration . Benutzer können Iteratoren schreiben, die einen Iterator in ihren Konstruktor aufnehmen und dessen Funktionalität erweitern:
template<class InputIterator, typename T>
class FilterIterator
{
private:
InputIterator internalIterator;
public:
FilterIterator(const InputIterator &iterator):
internalIterator(iterator)
{
}
virtual bool condition(T) = 0;
FilterIterator<InputIterator, T>& operator++()
{
do {
++(internalIterator);
} while (!condition(*internalIterator));
return *this;
}
T operator*()
{
// Needed for the first result
if (!condition(*internalIterator))
++(*this);
return *internalIterator;
}
virtual bool operator!=(const FilterIterator& that) const
{
return internalIterator != that.internalIterator;
}
};
template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
EvenIterator(const InputIterator &internalIterator) :
FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
{
}
bool condition(std::uint_fast32_t n)
{
return !(n % 2);
}
};
int main()
{
// Rolls a d20 until a 20 is rolled and discards odd rolls
EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
printAll(firstRandom, secondRandom);
return 0;
}
Obwohl diese Spielzeuge banal erscheinen mögen, ist es nicht schwer vorstellbar, Iteratoren und Iteratordekoratoren zu verwenden, um leistungsstarke Dinge mit einer einfachen Oberfläche zu erledigen - beispielsweise das Dekorieren eines Nur-Vorwärts-Iterators von Datenbankergebnissen mit einem Iterator, der beispielsweise ein Modellobjekt aus einem einzelnen Ergebnis erstellt . Diese Muster ermöglichen eine speichereffiziente Iteration unendlicher Mengen und mit einem Filter wie dem oben beschriebenen eine möglicherweise verzögerte Auswertung der Ergebnisse.
Ein Teil der Leistungsfähigkeit von C ++ - Vorlagen besteht darin, dass Ihre Iteratorschnittstelle, wenn sie auf C-Arrays mit fester Länge angewendet wird, in einfache und effiziente Zeigerarithmetik zerfällt , was sie zu einer wirklich kostengünstigen Abstraktion macht.
some_iterator++
zu++some_iterator
. Nach dem Inkrementieren wird ein unnötiger temporärer Iterator erstellt.