# Глава 7.1. Какие бывают контейнеры Контейнер — это коллекция элементов, реализованная как шаблон класса или структуры. Но в C++ не каждый шаблонный класс для хранения элементов может считаться контейнером. Контейнеры — это конкретные классы и структуры стандартной библиотеки. Их выбор разнообразен: есть массивы, хеш-таблицы, очереди и многое другое. Шаблоны разных контейнеров имеют разный набор параметров. Но среди них всегда есть тип элемента. Вам не обязательно досконально изучать интерфейс каждого из классов контейнеров, ведь всегда можно обратиться к [cppreference.](https://en.cppreference.com/w/cpp/container) Важнее уметь подбирать контейнер под конкретную задачу. А для этого полезно помнить: - Под какие сценарии оптимизирован контейнер. - Какова алгоритмическая сложность поддерживаемых им операций. - Какие структуры данных у него _скорее всего_ под капотом. Знание подкапотного устройства и алгоритмической сложности поможет находить ответы на вопросы: - Насколько возрастёт время доступа к элементу, если размер контейнера увеличится с сотни до десятков миллионов элементов? - Почему в каких-то случаях вставка элемента работает быстро, а в каких-то — медленно? - Какие контейнеры дружелюбны к кешу процессора ([cache-friendly](https://web.cecs.pdx.edu/~jrb/cs201/lectures/cache.friendly.code.pdf)), а какие — нет? Стандарт C++ фиксирует публичный интерфейс контейнеров, ограничения на алгоритмическую сложность операций и некоторые свойства. Например, в какой последовательности хранятся элементы. У стандартной библиотеки C++ больше одной реализации. Но многие реализации контейнеров схожи из-за налагаемых стандартом требований. ## Классификация контейнеров Контейнеры делятся на три категории, по одной на каждый из основных сценариев использования: - последовательные, - упорядоченные ассоциативные, - неупорядоченные ассоциативные. В **последовательных** (sequence) контейнерах элементы хранятся линейно. Место элемента зависит от того, когда и на какую позицию он был добавлен. И не зависит от значения самого элемента. Вы уже [познакомились](/courses/cpp/chapters/cpp_chapter_0061/#block-vector) с типичным представителем последовательных контейнеров — динамическим массивом `std::vector`. Поверх последовательных контейнеров реализованы **адаптеры.** Это классы, внутри использующие контейнеры, но предоставляющие над ними другой интерфейс. Например, поверх вектора организованы такие полезные структуры данных как [стек,](https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BA) [куча](https://ru.wikipedia.org/wiki/%D0%9A%D1%83%D1%87%D0%B0_(%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85)) и [очереди.](https://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)) В **ассоциативных** (associative) контейнерах позиция элемента зависит от его ключа. Эти контейнеры предназначены для хранения пар «ключ-значение» или только ключей. Сопоставление ключу значения называется отображением (mapping) или ассоциацией (association). Иногда такие контейнеры называют словарями. Ассоциативные контейнеры бывают упорядоченными и неупорядоченными. **Упорядоченные** (ordered) ассоциативные контейнеры часто называют просто ассоциативными контейнерами. Их элементы всегда отсортированы, а вставка, удаление и поиск элемента осуществляются за `O(log(N))`. **Неупорядоченные** (unordered) ассоциативные контейнеры часто называют хеш-таблицами. В них элементы, напротив, не отсортированы. Любое действие с элементом выполняется за `O(1)` в среднем и `O(N)` в худшем случае. ## Строка — это контейнер? Строка `std::string` во многом схожа с контейнерами стандартной библиотеки, однако не считается таковым. Строку иногда называют [псевдо-контейнером](https://en.cppreference.com/w/cpp/container) (pseudo container). Причина тому — ограничения на типы данных, которые позволяет хранить строка. Да, она может состоять не только из символов `char`! Но об этом позже. Внутри строка организована примерно так же, как `std::vector`, о котором вы скоро узнаете. Напишите функцию `rearrange_words()`. Она принимает строку, которая состоит из разделённых пробелами слов. Функция должна вернуть строку, содержащую те же слова, но в обратном порядке. Например, строка `not a bug` превратится в `bug a not`. {.task_text} Строка не может начинаться или заканчиваться пробелом. Одно слово отделяется от другого единственным пробелом. {.task_text} Воспользуйтесь итераторами, функциями [std::find()](https://en.cppreference.com/w/cpp/algorithm/find) и [std::reverse().](https://en.cppreference.com/w/cpp/algorithm/reverse) {.task_text} Эта задача имеет короткое и изящное решение. Если оно не приходит вам в голову, прочтите подсказку. {.task_text} ```cpp {.task_source #cpp_chapter_0071_task_0060} std::string rearrange_words(std::string s) { } ``` Сначала разверните строку целиком. Строка `not a bug` превратится в `gub a ton`. Затем разверните каждое слово по отдельности: `bug a not`. {.task_hint} ```cpp {.task_answer} std::string rearrange_words(std::string s) { std::reverse(s.begin(), s.end()); auto it_word = s.begin(); while (true) { auto it_space = std::find(it_word, s.end(), ' '); std::reverse(it_word, it_space); if (it_space == s.end()) break; it_word = it_space + 1; } return s; } ``` ## Другие классы, которые похожи на контейнеры Также контейнерами _не_ являются [std::bitset](https://en.cppreference.com/w/cpp/utility/bitset) и [std::valarray](https://en.cppreference.com/w/cpp/numeric/valarray). Класс `std::bitset` — это статический массив битов. А `std::valaray` — динамический массив, заточенный под математические вычисления. Этот класс не пользуется популярностью: он появился в C++98 и не отличается удобным интерфейсом. Вместо него для математических расчётов обычно подключают классы из специализированных библиотек, таких как [Eigen](https://eigen.tuxfamily.org/index.php?title=Main_Page) и [Armadillo](https://arma.sourceforge.net/). ---------- ## Резюме Классификация контейнеров стандартной библиотеки: ![Классификация контейнеров](https://raw.githubusercontent.com/senjun-team/senjun-courses/refs/heads/main/illustrations/cpp/containers.jpg) {.illustration}
Отправка...
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!