- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
Шрифт:
Интервал:
Закладка:
□ построение собственного итерабельного диапазона данных;
□ обеспечение совместимости ваших итераторов с категориями итераторов STL;
□ использование оболочек для итераторов для заполнения обобщенных структур данных;
□ реализация алгоритмов с помощью итераторов;
□ перебор (итерирование) в обратную сторону с применением обратных адаптеров для итераторов;
□ завершение перебора диапазонов данных с использованием ограничителей;
□ автоматическая проверка кода итератора с помощью проверяемых итераторов;
□ создание собственного адаптера для итераторов-упаковщиков.
Введение
Итераторы — крайне важная концепция языка С++. Библиотека STL создавалась максимально гибкой и обобщенной, а итераторы способствуют этому. К сожалению, иногда применять их несколько утомительно, и многие новички избегают их и возвращаются к стилю программирования, свойственному языку С. Программист, который избегает использования итераторов, по сути, отказывается от половины потенциала библиотеки STL. В данной главе мы рассмотрим итераторы, постаравшись разобраться в их достоинствах и недостатках. Надеюсь, показанные примеры помогут вам понять основные принципы работы с итераторами.
Многие классы-контейнеры, а также массивы в стиле С так или иначе содержат диапазон неких элементов. Для выполнения множества повседневных задач, связанных с обработкой больших объемов данных, не нужно знать, как эти данные были получены. Однако если у нас есть, например, массив целых чисел и список, содержащий целые числа, то следует воспользоваться двумя разными алгоритмами, представленными ниже.
□ Один алгоритм, который работает с массивом, проверяя его размер и суммируя все его члены, выглядит так:
int sum {0};
for (size_t i {0}; i < array_size; ++i) { sum += array[i]; }
□ Другой алгоритм, который работает со связанным списком и итерирует по нему до конца, выглядит следующим образом:
int sum {0};
while (list_node != nullptr) {
sum += list_node->value; list_node = list_node->next;
}
Оба алгоритма суммируют целые числа, но какая часть введенных нами символов непосредственно связана с решением задачи? Работает ли какой-нибудь из этих алгоритмов с другими видами структур данных, например с std::map, или нужно реализовывать еще одну версию алгоритма суммирования? Отсутствие итераторов приведет к нелепым решениям.
Только с помощью итераторов можно реализовать этот алгоритм в обобщенном виде:
int sum {0};
for (int i : array_or_vector_or_map_or_list) { sum += i; }
Это красивое и короткое выражение, названное «основанный на диапазоне цикл for», существует еще со времен С++11. Оно представляет собой лишь синтаксический сахар, который развертывается в нечто похожее на следующий код:
{
auto && range = array_or_vector_or_map_or_list;
auto begin = std::begin( range);
auto end = std::end( range);
for ( ; begin != end; ++ begin) {
int i = * begin;
sum += i;
}
}
Такие циклы хорошо знакомы всем, кто уже работал с итераторами, но кажутся черной магией для тех, кто еще этого не делал. Представьте, что наш вектор, содержащий целые числа, выглядит следующим образом (рис. 3.1).
Команда std::begin(vector) аналогична команде vector.begin(), она возвращает итератор, который указывает на первый элемент (1). Команда std::end(vector) аналогична команде vector.end(), она возвращает итератор, указывающий на элемент, стоящий за последним элементом (5).
На каждой итерации цикл проверяет, равен ли начальный итератор конечному. Если это не так, то мы разыменовываем начальный итератор и получаем доступ к числовому значению, на которое он указывает. Далее выполняем операцию инкремента для итератора, повторяем сравнение с конечным итератором и т.д. В этот момент полезно прочесть код цикла снова, представляя, что итераторы — обычные указатели, взятые из языка С. Фактически такие указатели тоже являются итераторами.
Категории итераторов
Существует несколько категорий итераторов, каждая из которых имеет разные ограничения. Их несложно запомнить, однако имейте в виду: возможности, требуемые одной категорией, унаследованы из другой, более мощной. Вся суть категорий итераторов заключается в том, что если при реализации алгоритма вы знаете, с каким именно итератором будете работать, то сможете реализовать оптимизированную версию. Таким образом, программисту достаточно просто выразить свое намерение, а компилятор выберет оптимальную реализацию для поставленной задачи.
Рассмотрим их в правильном порядке (рис. 3.2).
Итераторы ввода
Могут быть разыменованы только для того, чтобы прочесть значение, на которое указывают. При их инкрементировании последнее значение, на которое они указывают, становится недействительным во время данной операции, а значит, вы не можете итерировать по такому диапазону данных несколько раз. Примером итератора этой категории является std::istream_iterator.
Однонаправленные итераторы
Аналогичны итераторам ввода, но отличаются тем, что по диапазонам данных, которые они представляют, вы можете проитерировать несколько раз. В качестве примера такого итератора приведем std::forward_list. По такому списку можно проитерировать только вперед, но не назад, однако это можно сделать требуемое количество раз.
Двунаправленные итераторы
Как следует из названия, их можно инкрементировать и декрементировать, что позволяет итерировать вперед и назад. Эту возможность, например, поддерживают итераторы для контейнеров std::list, std::set и std::map.
Итераторы с произвольным доступом
Позволяют перескакивать через несколько значений сразу вместо того, чтобы двигаться пошагово. Примером таких итераторов являются итераторы для контейнеров std::vector и std::deque.
Непрерывные итераторы
Соответствуют всем указанным выше требованиям, но при этом необходимо, чтобы данные, по которым выполняется итерирование, находились в непрерывной памяти, как, например, в массиве std::vector.
Итераторы вывода
Вынесены в отдельную категорию. Причина такова: итератор может быть чистым итератором вывода, который можно только инкрементировать и использовать для записи данных в указываемое им место. При выполнении операции чтения значение будет неопределенным.
Изменяемые итераторы
Если итератор является итератором вывода, а также состоит в какой-то другой категории, то называется изменяемым. С его помощью можно считывать и записывать данные. При получении из неконстантного экземпляра контейнера итератор, как правило, будет состоять именно в этой категории.
Создаем собственный итерабельный диапазон данных
Мы уже знаем, что итераторы в некоторой степени представляют собой стандартный интерфейс для выполнения перебора во всех видах контейнеров. Нужно только реализовать оператор префиксного инкремента ++, оператор разыменования * и оператор сравнения объектов ==, и получится примитивный итератор, подходящий для работы с циклом for, основанным на диапазоне, который появился в C++11.
Чтобы немного освоиться с итераторами, рассмотрим пример реализации одного из них, который просто генерирует диапазон чисел