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, который указывает на элемент, находящийся перед первым элементом. Вот как это выглядит.

for (list::reverse_iterator p = lstStr.rbegin();

 p != lstStr.rend(); ++p) {

 cout << *p << endl;

}

Но может возникнуть ситуация, когда использовать reverse_iterator невозможно. В этом случае используйте обычный iterator, как здесь.

for (list::iterator p = --lstStr.end();

 p != --lstStr.begin(); --p) {

 cout << *p << endl;

}

Наконец, если вы знаете, на сколько элементов вперед или назад следует выполнить перебор, используйте вычисление значения, на которое следует перевести итератор. Например, чтобы перейти в середину списка, сделайте вот так.

size_t i = lstStr.size();

list::iterator p = begin();

p += i/2; // Переход к середине последовательности

Но помните: в зависимости от типа используемого контейнера эта операция может иметь как постоянную, так и линейную сложность. При использовании контейнеров, которые хранят элементы последовательно, таких как vector или deque, iterator может перейти на любое вычисленное значение за постоянное время. Но при использовании контейнера на основе узлов, такого как list, такая операция произвольного доступа недоступна. Вместо этого приходится перебирать все элементы, пока не будет найден нужный. Это очень дорого. Именно поэтому выбор контейнера, используемого в каждой конкретной ситуации, определяется требованиями к перебору элементов контейнера и их поиска в нем. (За более подробной информацией о работе стандартных контейнеров обратитесь к главе 6.)

При использовании контейнеров, допускающих произвольный доступ, для доступа к элементам использования operator[] с индексной переменной следует предпочитать iterator. Это особенно важно при написании обобщенного алгоритма в виде шаблона функции, так как не все контейнеры поддерживают iterator с произвольным доступом.

С итератором можно делает еще много чего, но не с любым iterator. iterator может принадлежать к одной из пяти категорий, обладающих разной степенью функциональности. Однако они не так просты, как иерархия классов, так что именно это я далее и опишу.

Категории итераторов

Итераторы, предоставляемые различными типами контейнеров, не обязательно все умеют делать одно и то же. Например, vector::iterator позволяет использовать для перехода на некоторое количество элементов вперед operator+=, в то время как list::iterator не позволяет. Разница между этими двумя типами итераторов определяется их категорией.

Категории итераторов — это, по сути, интерфейс (не технически; для реализации категорий итераторов абстрактные базовые классы не используются). Имеется пять категорий, и каждая предлагает увеличение возможностей. Вот как они выглядят — от наименее до наиболее функциональной.

Input iterator (Итератор ввода)

Итератор ввода поддерживает переход вперед с помощью p++ или ++p и разыменовывание с помощью *p. При его разыменовывании возвращается rvalue, iterator ввода используется для таких вещей, как потоки, где разыменовывание итератора ввода означает извлечение очередного элемента из потока, что позволяет прочесть только один конкретный элемент.

Output iterator (Итератор вывода)

Перейти на страницу:

Похожие книги