C++. Сборник рецептов - [97]

Шрифт
Интервал

Использование итераторов

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

>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) {


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


Создаем порт для FreeBSD своими руками. Часть II

Система сборки программ, используемая во FreeBSD, имеет значительно большие возможности, чем те, которые мы задействовали. Какие это возможности и как их использовать в своих портах?


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.


Как пасти котов. Наставление для программистов, руководящих другими программистами

«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.