Лекция 29. Внутреннее устройство deque. Итераторы. Виды итераторов

YOUTUBE · 29.11.2025 05:09

Ключевые темы и таймкоды

Введение в тему

0:00
  • Обсуждение реализации пушбэка в векторе и его недостатков.
  • Упоминание о векторе буле и его особенностях.
  • Переход к обсуждению дека.

Пример с вектором

0:58
  • Демонстрация примера с вектором и указателем.
  • Объяснение проблемы с разыменованием указателя после пушбэка.

Инвалидация указателей

2:01
  • Объяснение явления инвалидации указателей.
  • Объяснение причины инвалидации: релокация вектора.

Инвалидация ссылок

3:57
  • Обсуждение инвалидации ссылок на элементы вектора.
  • Подчёркивание распространённости ошибки среди новичков.

Свойства вектора

6:42
  • Вектор инвалидирует указатели и ссылки после изменения.
  • Исключение: попбэк не инвалидирует указатели.

Особенности дека

7:46
  • Дек не инвалидирует указатели и ссылки при добавлении элементов.
  • Преимущества дека для контейнеров стек, кью, приорити.

Реализация дека

10:20
  • Обсуждение внутренней структуры дека.
  • Необходимость поддержки пушбэка, пушфронта и попфронта.

Структура дека

12:14
  • Описание структуры дека: массив указателей и массив элементов.
  • Процесс выделения памяти при добавлении элементов.

Ограничения вектора

14:10
  • Невозможность реализации дека только с помощью вектора.
  • Сложности реализации дека с использованием векторов.

Основы работы с деком

15:08
  • Необходимо хранить информацию о начале и конце текущего дека.
  • При релокации массива указатели не инвалидируются.
  • Релокация происходит при заполнении массива, при этом элементы остаются на своих местах.

Реализация операций с деком

16:50
  • Для добавления элементов используется `placement new`.
  • При удалении элемента необходимо вызвать деструктор.
  • Индексация в деке начинается с нуля.

Методы вектора и дека

19:13
  • У дека отсутствуют методы `shrink_to_fit`, `reserve` и `capacity`.
  • Дек сам управляет памятью, не требуя дополнительных методов.

Адаптеры над контейнерами

19:59
  • `std::stack`, `std::queue` и `std::priority_queue` — это адаптеры над контейнерами.
  • По умолчанию они работают с деком, но могут быть адаптированы под другие контейнеры.
  • Адаптеры переадресовывают свои методы в методы соответствующего контейнера.

Особенности `std::stack`

21:01
  • `std::stack` — это шаблон с фиксированным типом второго параметра.
  • Второй параметр может быть заменён на другой контейнер.
  • Метод `pop` возвращает `void`, а не `T`, чтобы избежать копирования элементов.

Введение в итераторы

26:34
  • В связанных списках и картах индексация не всегда возможна.
  • Итераторы позволяют обходить последовательности без использования индексов.
  • Итератор — это тип, который можно разыменовывать, инкрементировать и сравнивать на равенство.

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

30:21
  • Итераторы подразделяются на категории в зависимости от дополнительных возможностей.
  • Минималистичный итератор — это `input_iterator`.
  • Частным случаем `input_iterator` является `forward_iterator`.

Введение в форвард-итераторы

31:13
  • Форвард-итератор гарантирует, что при повторном прохождении последовательности элементы будут одинаковыми.
  • Импут-итератор не гарантирует этого свойства.
  • Термины «импут-итератор» и «форвард-итератор» относятся к свойствам типов, а не к конкретным типам стандартной библиотеки.

Различие между импут- и форвард-итераторами

32:07
  • Форвард-итератор дополнительно гарантирует, что при повторном прохождении последовательности элементы будут оставаться неизменными.
  • Пример: односвязный список.

Би-дирекшн-итераторы

33:35
  • Би-дирекшн-итератор позволяет двигаться вперёд и назад.
  • Пример: двусвязный список.

Итераторы в контейнерах

34:01
  • В каждом контейнере стандартной библиотеки есть внутренний тип-итератор.
  • Форвард-лист и односвязный список являются примерами контейнеров с форвард-итераторами.

Рендом-акцесс-итераторы

36:13
  • Рендом-акцесс-итератор поддерживает операции сложения и вычитания с числом.
  • Примеры: вектор, карта.

Контигиус-итераторы

38:04
  • Контигиус-итератор эквивалентен указателю и позволяет эффективно перемещаться по памяти.
  • Примеры: вектор, указатель.
  • В деке итератор не является контигиус-итератором, так как элементы в памяти не лежат подряд.

Ренч-бейс-фор

40:34
  • Ренч-бейс-фор позволяет эффективно работать с диапазонами элементов.
  • Синтаксис «фор инт x: контейнер» — это синтаксический сахар, сокращающий запись.
  • Для работы ренч-бейс-фора необходимо определить методы «бегин» и «энд».

Формальные свойства контейнеров

44:09
  • Контейнер должен иметь внутренние типы: вел референс, конст-референс, итератор, констатератор, дифференс тайп и сай-тайп.
  • Итератор должен удовлетворять требованиям форвард-итератора.
  • Контейнер должен уметь создаваться пустым, копироваться, мутироваться, присваиваться, вызывать деструктор, иметь методы «бегин», «энд», «сравниться», «свопиться», «сайз», «макс-сайз» и «эмти».

Введение в итераторы

46:31
  • Обсуждение проблем с вычислением размера в связанных списках.
  • Определение итератора и его свойств, предваряемых словом «легаси».
  • Упоминание о незначительных изменениях в стандарте C++20.

Свойства итераторов

47:57
  • Итератор должен поддерживать копирование, деструктор и своп.
  • Возможность использования звёздочки для получения ссылки на итератор.
  • Импутатор дополнительно поддерживает сравнение на равенство и неравенство, стрелочку и постфиксный инкремент.

Ренч бейс фор

48:52
  • Описание сайта cpp-analyzer для анализа начальной стадии компиляции.
  • Пример преобразования кода с использованием ренч бейс фор для массива.
  • Создание указателей на начало и конец массива.

Преобразование кода

50:10
  • Пример преобразования кода с набором.
  • Подстановка шаблонных параметров компаратор и локатор.
  • Преобразование синтаксического сахара в базовые операции.

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

52:36
  • Обзор стандартных функций для работы с последовательностями, принимающих итераторы.
  • Пример функции sort и её требования к итератору.
  • Ошибка компиляции при попытке использовать не-рендом аксетератор.

Классификация алгоритмов по итераторам

55:41
  • Алгоритмы классифицируются по требованиям к итераторам.
  • Примеры алгоритмов, требующих разные типы итераторов: quicksort, bubble sort, next permutation.
  • Объяснение различий между форвардером и импутатором.

Особые виды итераторов

59:28
  • Аудотератор как особый вид итератора, позволяющий писать в последовательность.
  • Обсуждение задач по алгоритмам, формулируемых в терминах итераторов.
  • Упоминание аттератора для возможности записи и сдвига вперёд.

Задачи с итераторами

1:00:39
  • Задача: в массиве все числа встречаются дважды, кроме одного, которое встречается только один раз.
  • Решение: отсортировать элементы и найти единственный уникальный элемент.
  • Итераторы позволяют решить задачу за один проход по последовательности.

Задача с числом, встречающимся более половины раз

1:01:36
  • Задача: найти число в массиве, которое встречается более половины раз.
  • Условие: задача должна быть решена за линейное время с использованием только итератора.
  • Аналогия с танцами: при каждом прохождении массива сравниваются числа, и если они совпадают, то одно из них «отдаётся» в пару, уменьшая количество повторений.

Алгоритм бинарного поиска

1:04:01
  • Алгоритм бинарного поиска требует форвард-итератора для эффективного выполнения.
  • Подвох: бинарный поиск работает за логарифмическое время относительно операций на T, но сдвиги итератора не учитываются.
  • Пример: на массиве длинных строк сравнение может быть дорогой операцией, поэтому использование форвард-итератора может быть оправдано.

Метод push_back в деке

1:07:13
  • Метод push_back в деке работает за константное время относительно операций на T.
  • В векторе амортизированная константа, так как требуется вызов деструкторов и конструкторов на новом месте.
  • Сложность операций в стандарте оценивается относительно операций на T, а не операций над сырой памятью.

Определение типа под итератором

1:10:14
  • Проблема: как определить тип, который лежит под итератором?
  • Пример с вектором bool показывает, что использование `auto` может дать неверный результат.
  • Решение: использование метафункции `iterator_traits` для получения информации о типе под итератором.

Метафункция `iterator_traits`

1:14:55
  • `iterator_traits` позволяет узнать много информации об итераторе, включая тип.
  • Пример использования: `iterator_traits<T>::value_type`.
  • Важно использовать `typename` для получения зависимого имени типа.

Введение в итератор трейси

1:16:13
  • Обсуждение возможности реализации итератор трейси с помощью шаблонной магии.
  • Упоминание о возможности узнать типы итератора: вел-ю тайп, референс тайп и другие.

Основные компоненты итератор трейси

1:16:55
  • Описание вел-ю тайпа как обязательного компонента для определения типов итератора.
  • Объяснение понятия пойнтера и его использования.
  • Роль референса в контексте векторов и бит референса.
  • Введение понятия итератор кэтигория для определения категории итератора.

Реализация итератор трейси в разных версиях C++

1:17:52
  • Объяснение работы метафункции итератор трейси для определения вида итератора.
  • Упоминание о сложности реализации в версиях C++ до C++20.
  • Описание итератор кэтигория как специального типа, обозначающего категории итераторов.

Пустые структуры и теги

1:18:51
  • Объяснение пустых структур, таких как итератор тэг и дирекшн тэк, в контексте категорий итераторов.
  • Роль тегов в проверке равенства типов.
  • Пример использования тегов для определения категории итератора и выполнения соответствующих действий.

Завершение

1:19:40
  • Предложение сделать перерыв.