STL Итераторы Категории итераторов

Продолжая знакомство с итераторами, посмотрим на различные их категории.
Итераторы подчиняются принципу чистой абстракции, то есть любой объект, который ведет себя как итератор, является итератором.
Категории итераторов:

  • Итераторы ввода и вывода
  • Однонаправленные итераторы и двунаправленные итераторы
  • Итераторы произвольного доступа
  • Вспомогательные итераторы
  • Реверсивные итераторы
  • Итераторы потоков
  • Итераторы вставки
  • Константные итераторы
  • Итератор ввода — это такой итератор, который перемещается только вперед и поддерживает только чтение.
Итератор ввода — это самая простая разновидность итераторов. Доступны такие итераторы только для чтения. Чтобы узнать прочтённый элемент, к итератору применяется оператор разыменования (как с указателем). Иногда может потребоваться узнать, указывают ли два итератора ввода на один и тот же объект, для этого разрешено сравнивать два итератора ввода между собой. Вместо итератора ввода может подставляться любой основной итератор кроме итератора вывода.
В моём представлении самым простым примером итератора ввода будет вывод на экран элементов массива с помощью итератора ввода:

for_each — это алгоритм. Алгоритм for_each принимает указатель на начало массива, указатель на первый элемент за границей массива и указатель на функцию. Не путайте саму функцию с указателем на нее. Кто не понял, при чем тут итератор, читайте самые первые строки. Еще может кому-то не очень понятен параметр num. В этот параметр записывается значение, которое получил алгоритм for_each. Подробней про for_each написано в теме C++ для начинающих. Алгоритм for_each
В качестве ещё одного примера можно взять заполнение контейнера STL из текстового файла (заполнение с помощью итератора ввода).

Могут возникнуть вопросы: &quotlчто такое istreambuf_iterator?" — это почти такой же итератор, как istream_iterator. А что такое "istream_iterator?" — это итератор входного потока, который читает (с помощью operator <<) последовательные элементы из входного потока, для которого создан.
Чтобы никто не запутался, уточню

  • istream_iterator — итератор для чтения элементов из входного потока (использует операцию <<).
  • streambuf_iterator — итератор, похожий на istream_iterator, и работает аналогично, но объекты итератора istream_iterator обращаются прямо к буферу потока и читают непосредственно следующий символ.
Это работает приблизительно так: первый вариант читает до первого пробела в потоке, а второй читает весь поток.
  • Невозможно записывать что-либо использованием входных итераторов!
Итераторы вывода — это противоположность итераторам ввода. Служат они для ссылки на области памяти, куда выводятся данные. Разыменовывать такие итераторы нужно только для того, чтобы присваивать некие значения объектам, на который итераторы ссылаются. Итераторы ввода могут быть возвращены итераторами потоков вывода (ostream_iterator) и итераторами вставки inserter, front_inserter и back_inserter.
Как и в первом случае, приведу два примера: для массива и для файла.

В файл будет записана строка: "1,2,3".
Разумеется, работать можно не только с файлами, но и с любыми потоками вообще. Используем итератор вывода для вывода всех элементов вектора на экран. Похожее уже было, но мало ли кто не понял. Выводим элементы вектора в поток cout:

Не путайте copy с итератором, copy — это алгоритм и for_each тоже алгоритм. ostream_iterator — итератор.
  • Если объединить итератор ввода и итератор вывода, то получится однонаправленный итератор.
  • Однонаправленный итератор — это итератор, который поддерживает чтение и запись адресуемого элемента.
Как один из понятных примеров, могу привести пример поиска элемента в массиве:

Чтобы немного конкретизировать, поясню. Сначала мы запоминаем значение в x, потом это значение даём алгоритму find как один из параметров. После этого алгоритм ищет этот полученный для поиска элемент x в обозначенном диапазоне. Если поиски были удачны, то прочитанный алгоритмом элемент выводится на экран, если же поиски неудачны, то попытки вывести элемент (presult) с очень большой вероятностью приведут к выводу мусора. Так как для одного и того же алгоритма были произведены и ввод в алгоритм и вывод элемента из алгоритма, при этом происходил последовательный перебор элементов, то, согласно определению, это можно называть однонаправленным итератором.
Еще один пример однонаправленного итератора — использование такого в алгоритме replace. Проведем небольшую перестановку в массиве:

Алгоритм replace читает значения, изменяет их и производит внутреннее перемещение итератора от одного элемента к другому.
Есть и другие примеры, но к тем, кто не очень понял, лучшее понимание прийдет со временем, главное заниматься

  • Двунаправленные итераторы аналогичны однонаправленным и широко применяются в реверсивных алгоритмах.

  • Итераторы контейнерных классов list, set, multiset, map и multimap являются двунаправленными.
Двунаправленные итераторы способны перемещаться в обоих направлениях благодаря инкременту (++) и декременту (— —).
Итераторы произвольного доступа имеют большие возможностей по сравнению со всеми остальными основными итераторами. Использование таких итераторов очень и очень сильно похоже на использование арифметики указателей.

Т. е. итераторы произвольного доступа — это такие итераторы, которые не только дают легко обращаться к произвольному элементу. Несмотря на то, что мы изменяем итератор переприсваиванием в него значения, на контейнер это никак не влияет.
Реверсивные итераторы — это такие итераторы, которые помогают делать обходы наоборот. Надеюсь, с понятием реверсивности вы знакомы. Получить реверсивные итераторы для контейнеров можно добавлением буквы r к begin() и end(): rbegin(), rend().

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

Надеюсь, комментарии дадут все понять, итак много как-то вышло написать.
Еще есть константный итератор. Константный итератор не допускает изменений в данных, на которые ссылается. Можно считать константный итератор указателем на константу.

Такое вот знакомство с итераторами получилось. В принципе, не очень сложно, но и не сказать, что совсем просто. Много непонятных моментов, многое может запутать. Главное — заниматься, и все станет на свои места. Цель этого материала не досконально объяснить итераторы, а дать обобщенное понятие и себе и читателю, чтоб было легче ориентироваться в понятиях итераторов. Досконально объяснить я все равно не могу.
Добавлю в публикацию эту небольшую таблицу:
Итератор Описание
для чтения Читают значения с движением вперед. Могут быть инкрементированы, сравнены и разыменованы.
для записи Пишут значения с движением вперед. Могут быть инкрементированы и разыменованы.
однонаправленные Читают или пишут значения с движением вперед. Комбинируют функциональность предыдущих двух типов с возможностью сохранять значение итератора.
двунаправленные Читают и пишут значения с движением вперед или назад. Похожи на однонаправленные, но их также можно инкрементировать и декрементировать.
с произвольным доступом Читают и пишут значения с произвольным доступом. Самые мощные итераторы, сочетающие функциональность двунаправленных итераторов и возможность выполнения арифметики указателей и сравнений указателей.
обратные Или итераторы с произвольным доступом, или двунаправленные, движущиеся в обратном направлении.
Итераторы для существующих элементов в контейнере могут стать недействительными после изменения контейнера. Это делает изменение контейнера при итерациях проблематичной. Контейнеры предлагают различные гарантии в этой связи:
vector: вставка / удаление может аннулировать все итераторы.
list/map: удаление делает недействительными только итераторы на удаленный элемент(ы), но не на другие элементы.

3 комментария на «“STL Итераторы Категории итераторов”»

  1. Василь:

    Прошу прощения, что-то с тегами не вышло 🙂

  2. Marry:

    А можно такой же разбор только с List?

    Я понимаю, что там всё похоже, но всё же.

    И желательно, чтобы использовалась структура, а не целые числа/чаровский массив.

    Буду очень признательна!

  3. Cast:

    Первый пример, строка 14

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/vadim-dunaev/samouchitel-javascript/?lfrom=15589587
Яндекс.Метрика