Использование итераторов
Итератор объявляется с помощью типа, элементы которого с его помощью будут перебираться. Например, в примере 7.1 используется list, так что итератор объявляется вот так.
list::iterator p = lstStr.begin();
Если вы не работали со стандартными контейнерами, то часть этого объявления ::iteratorможет выглядеть несколько необычно. Это вложенный в шаблон класса list typedef, предназначенный именно для этой цели — чтобы пользователи контейнера могли создать итератор для данного конкретного экземпляра шаблона. Это стандартное соглашение, которому следуют все стандартные контейнеры. Например, можно объявить итератор для listили для set, как здесь.
list::iterator p1;
set::iterator p2;
Возвращаясь обратно к нашему примеру, итератор о инициализируется первым элементом последовательности, который возвращается методом begin. Чтобы перейти к следующему элементу, используется operator++. Можно использовать как префиксный инкремент так и постфиксный инкремент ( p++), аналогично указателям на элементы массивов, но префиксный инкремент не создает временного значения, так что он более эффективен и является предпочтительным. Постфиксный инкремент ( p++) должен создавать временную переменную, так как он возвращает значение pдо его инкрементирования. Однако он не может инкрементировать значение после того, как вернет его, так что он вынужден делать копию текущего значения, инкрементировать текущее значение, а затем возвращать временное значение. Создание таких временных переменных с течением времени требует все больших и больших затрат, так что если вам не требуется именно постфиксное поведение, используйте префиксный инкремент.
Как только будет достигнут элемент end, переход на следующий элемент следует прекратить. Или, строго говоря, когда будет достигнут элемент, следующий за end. В отношении стандартных контейнеров принято некое мистическое значение, которое представляет элемент, идущий сразу за последним элементом последовательности, и именно оно возвращается методом end. Этот подход работает в цикле for, как в этом примере:
for (list::iterator p = lstStr.begin();
p != lstStr.end(); ++p) {
cout << *p << endl;
}
Как только pстанет равен end, pбольше не может увеличиваться. Если контейнер пуст, то begin == endравно true, и тело цикла никогда не выполнится. (Однако для проверки пустоты контейнера следует использовать метод empty, а не сравнивать beginи endили использовать выражение вида size == 0.)
Это простое объяснение функциональности итераторов, но это не все. Во-первых, как только что было сказано, итератор работает как rvalueили lvalue, что означает, что его разыменованное значение можно присваивать другим переменным, а можно присвоить новое значение ему. Для того чтобы заменить все элементы в списке строк, можно написать нечто подобное следующему
for (list::iterator p = lstStr.begin();
p != lstStr.end(); ++p) {
*p = "mustard";
}
Так как *pссылается на объект типа string, для присвоения элементу контейнера новой строки используется выражение string::operator=(const char*). Но что, если lstStr — это объект типа const? В этом случае iteratorне работает, так как его разыменовывание дает не-const объект. Здесь требуется использовать const_iterator, который возвращает только rvalue. Представьте, что вы решили написать простую функцию для печати содержимого контейнера. Естественно, что передавать контейнер следует как const-ссылку.
template
void printElements(const T& cont) {
for(T::const_iterator p = cont.begin();
p ! = cont.end(); ++p) {
cout << *p << endl;
}
}
В этой ситуации следует использовать именно const, a const_iteratorпозволит компилятору не дать вам изменить *p.
Время от времени вам также может потребоваться перебирать элементы контейнера в обратном порядке. Это можно сделать с помощью обычного iterator, но также имеется reverse_iterator, который предназначен специально для этой задачи. reverse_iteratorведет себя точно так же, как и обычный iterator, за исключением того, что его инкремент и декремент работают противоположно обычному iteratorи вместо использования методов beginи endконтейнера с ним используются методы rbeginи rend, которые возвращают reverse_iterator. reverse_iteratorпозволяет просматривать последовательность в обратном порядке. Например, вместо инициализации reverse_iteratorс помощью beginон инициализируется с помощью rbegin, который возвращает reverse_iterator, указывающий на последний элемент последовательности. operator++перемещает его назад — по направлению к началу последовательности, rendвозвращает reverse_iterator, который указывает на элемент, находящийся перед первым элементом. Вот как это выглядит.
Читать дальше