Стандартная библиотека C++ |
---|
Контейнеры |
Стандартная библиотека C |
Стандартная библиотека шаблонов ( STL ) — это программная библиотека, изначально разработанная Александром Степановым для языка программирования C++ , которая повлияла на многие части стандартной библиотеки C++ . Она предоставляет четыре компонента, называемые алгоритмами , контейнерами , функциями и итераторами . [1]
STL предоставляет набор общих классов для C++, таких как контейнеры и ассоциативные массивы , которые могут использоваться с любым встроенным типом и с любым определяемым пользователем типом, который поддерживает некоторые элементарные операции (такие как копирование и присваивание). Алгоритмы STL не зависят от контейнеров, что значительно снижает сложность библиотеки.
STL достигает своих результатов посредством использования шаблонов . Этот подход обеспечивает полиморфизм времени компиляции , который часто более эффективен, чем традиционный полиморфизм времени выполнения . Современные компиляторы C++ настроены на минимизацию штрафов за абстракцию, возникающих при интенсивном использовании STL.
STL была создана как первая библиотека универсальных алгоритмов и структур данных для C++ с учетом четырех идей: универсальное программирование , абстрактность без потери эффективности, модель вычислений фон Неймана [2] и семантика значений .
STL и стандартная библиотека C++ — это две разные сущности. [3]
В ноябре 1993 года Александр Степанов представил библиотеку, основанную на обобщенном программировании, комитету ANSI/ISO по стандартизации C++. Ответ комитета был исключительно благоприятным и привел к запросу Эндрю Кенига на официальное предложение ко времени встречи в марте 1994 года. У комитета было несколько запросов на изменения и расширения, и члены комитета встретились со Степановым и Мэн Ли, чтобы помочь проработать детали. Требования к наиболее значимому расширению ( ассоциативные контейнеры ) должны были быть продемонстрированы как согласованные путем их полной реализации, задача, которую Степанов делегировал Дэвиду Массеру . Предложение получило окончательное одобрение на встрече комитета ANSI/ISO в июле 1994 года. Впоследствии документ 17 Степанова и Ли был включен в проект стандарта ANSI/ISO C++ (1, части пунктов 17–27).
Перспективы раннего широкого распространения STL значительно улучшились с решением Hewlett-Packard в августе 1994 года сделать ее реализацию свободно доступной в Интернете . Эта реализация, разработанная Степановым, Ли и Массером в процессе стандартизации, стала основой многих реализаций, предлагаемых сегодня поставщиками компиляторов и библиотек.
STL содержит последовательные контейнеры и ассоциативные контейнеры. Контейнеры — это объекты, которые хранят данные. Стандартные последовательные контейнеры включают vector
, deque
, и list
. Стандартные ассоциативные контейнеры — это set
, multiset
, map
, multimap
, hash_set
, и . Существуют также адаптеры контейнеров , , и , которые являются контейнерами со специальным интерфейсом, использующими другие контейнеры в качестве реализации.hash_map
hash_multiset
hash_multimap
queue
priority_queue
stack
Контейнер | Описание |
---|---|
Простые контейнеры | |
пара | Контейнер пар — это простой ассоциативный контейнер, состоящий из 2- кортежа элементов данных или объектов, называемых «первым» и «вторым», в этом фиксированном порядке. «Пару» STL можно назначать, копировать и сравнивать. Массив объектов, выделенных в map или hash_map (описанных ниже), по умолчанию имеет тип «пара», где все «первые» элементы действуют как уникальные ключи, каждый из которых связан со своим «вторым» объектом значения. |
Последовательности (массивы/ связанные списки ): упорядоченные коллекции | |
вектор | динамический массив , как массив C (т. е. способный к произвольному доступу ) с возможностью автоматического изменения своего размера при вставке или стирании объекта. Вставка элемента в конец вектора занимает амортизированное постоянное время . Удаление последнего элемента занимает только постоянное время, поскольку не происходит изменения размера. Вставка и стирание в начале или в середине линейны по времени. Существует специализация для типа bool , которая оптимизирует пространство, сохраняя значения bool в виде битов. |
список | двусвязный список ; элементы не хранятся в непрерывной памяти. Противоположная производительность по сравнению с вектором. Медленный поиск и доступ (линейное время), но после нахождения позиции быстрая вставка и удаление (постоянное время). |
список | односвязный список ; элементы не хранятся в непрерывной памяти. Противоположная производительность по сравнению с вектором. Медленный поиск и доступ (линейное время), но после нахождения позиции быстрая вставка и удаление (постоянное время). Он имеет немного более эффективную вставку и удаление и использует меньше памяти, чем двусвязный список, но может быть итерирован только вперед. Он реализован в стандартной библиотеке C++ как forward_list . |
deque ( двусторонняя очередь ) | вектор со вставкой/стиранием в начале или конце за амортизированное постоянное время , однако не имеющий некоторых гарантий относительно действительности итератора после изменения очереди. |
Адаптеры для контейнеров | |
очередь | Предоставляет интерфейс очереди FIFO с точки зрения операций /// .push pop front back Любая последовательность, поддерживающая операции , , , и может использоваться для создания очереди (например, и ). |
приоритетная очередь | Предоставляет интерфейс приоритетной очереди с точки зрения операций (элемент с наивысшим приоритетом находится сверху).push/pop/top Любая последовательность случайного доступа , поддерживающая операции , и может быть использована для создания priority_queue (например, и ). Она реализуется с использованием кучи . Элементы должны дополнительно поддерживать сравнение (чтобы определить, какой элемент имеет более высокий приоритет и должен быть извлечен первым). |
куча | Предоставляет интерфейс стека LIFO с точки зрения операций (последний вставленный элемент находится сверху).push/pop/top Любая последовательность, поддерживающая операции , , и , может использоваться для создания экземпляра стека (например , , , и ). |
Ассоциативные контейнеры : неупорядоченные коллекции | |
набор | математический набор ; вставка/стирание элементов в наборе не делает итераторы, указывающие на набор, недействительными. Предоставляет операции над множествами: объединение , пересечение , разность , симметричная разность и проверка включения. Тип данных должен реализовывать оператор сравнения < или должна быть указана пользовательская функция компаратора; такой оператор сравнения или функция компаратора должны гарантировать строгое слабое упорядочение , в противном случае поведение не определено. Обычно реализуется с помощью самобалансирующегося бинарного дерева поиска . |
мультисет | то же, что и набор, но допускает дублирование элементов (математический мультимножество ). |
карта | ассоциативный массив ; позволяет отображать один элемент данных (ключ) в другой (значение). Тип ключа должен реализовывать оператор сравнения < или должна быть указана пользовательская функция компаратора; такой оператор сравнения или функция компаратора должны гарантировать строгое слабое упорядочение , в противном случае поведение не определено. Обычно реализуется с использованием самобалансирующегося бинарного дерева поиска. |
мультикарта | то же, что и карта, но допускает дублирование ключей. |
hash_set hash_multiset hash_map hash_multimap | похоже на set, multiset, map или multimap соответственно, но реализовано с использованием хэш-таблицы ; ключи не упорядочены, но для типа ключа должна существовать хэш-функция . Эти типы были исключены из стандарта C++; подобные контейнеры были стандартизированы в C++11 , но с другими именами ( unordered_set и unordered_map ). |
Другие типы контейнеров | |
битсет | хранит ряд битов, аналогичный вектору bool фиксированного размера. Реализует побитовые операции и не имеет итераторов. Не является последовательностью. Предоставляет произвольный доступ. |
валаррей | Другой тип данных массива, предназначенный для числового использования (особенно для представления векторов и матриц ); стандарт C++ допускает определенные оптимизации для этой предполагаемой цели. По словам Джосуттиса, valarray был плохо спроектирован людьми, «которые покинули комитет [стандарта C++] задолго до того, как стандарт был закончен», и библиотеки шаблонов выражений должны быть предпочтительными. [4] Предложенное переписывание части стандарта valarray в этом ключе было отклонено, вместо этого став разрешением на его реализацию с использованием шаблона выражения. [5] |
STL реализует пять различных типов итераторов . Это итераторы ввода (которые могут использоваться только для чтения последовательности значений), итераторы вывода (которые могут использоваться только для записи последовательности значений), итераторы прямого ввода (которые могут быть прочитаны, записаны и перемещены вперед), двунаправленные итераторы (которые похожи на итераторы прямого ввода, но также могут перемещаться назад) иИтераторы с произвольным доступом (которые могут свободно перемещаться на любое количество шагов за одну операцию).
Фундаментальной концепцией STL является диапазон , представляющий собой пару итераторов, обозначающих начало и конец вычисления, и большинство алгоритмических шаблонов библиотеки, работающих со структурами данных, имеют интерфейсы, использующие диапазоны. [6]
Двунаправленные итераторы могут действовать как итераторы с произвольным доступом, поэтому перемещение вперед на десять шагов может быть выполнено простым перемещением вперед на один шаг за раз в общей сложности десять раз. Однако наличие отдельных итераторов с произвольным доступом дает преимущества в плане эффективности. Например, вектор будет иметь итератор с произвольным доступом, а список — только двунаправленный итератор.
Итераторы являются основной функцией, которая обеспечивает общность STL. Например, алгоритм для обращения последовательности может быть реализован с использованием двунаправленных итераторов, а затем та же реализация может быть использована для списков, векторов и deques . Созданные пользователем контейнеры должны только предоставить итератор, который реализует один из пяти стандартных интерфейсов итераторов, и все алгоритмы, предоставленные в STL, могут быть использованы для контейнера.
Эта общность иногда имеет свою цену. Например, выполнение поиска в ассоциативном контейнере , таком как map или set, может быть намного медленнее с использованием итераторов, чем при вызове функций-членов, предлагаемых самим контейнером. Это происходит потому, что методы ассоциативного контейнера могут использовать знание внутренней структуры, которая непрозрачна для алгоритмов, использующих итераторы.
В STL предусмотрено большое количество алгоритмов для выполнения таких действий, как поиск и сортировка, каждый из которых реализован для требования определенного уровня итератора (и, следовательно, будет работать на любом контейнере, который предоставляет интерфейс итераторами). Алгоритмы поиска, такие как binary_search
и lower_bound
используют бинарный поиск , и такие алгоритмы сортировки требуют, чтобы тип данных реализовывал оператор сравнения <
или была указана пользовательская функция компаратора; такой оператор сравнения или функция компаратора должны гарантировать строгое слабое упорядочение . Помимо этого, предусмотрены алгоритмы для создания кучи из диапазона элементов, генерации лексикографически упорядоченных перестановок диапазона элементов, слияния отсортированных диапазонов и выполнения объединения , пересечения , разности отсортированных диапазонов.
STL включает классы, которые перегружают оператор вызова функции ( ). Экземпляры таких классов называются функторами или функциональными объектами . Функторы позволяют параметризовать поведение связанной функции (например, с помощью аргументов, передаваемых конструктору функтора ) и могут использоваться для сохранения связанной информации о состоянии функтора вместе с функцией. Поскольку и функторы, и указатели функций могут вызываться с использованием синтаксиса вызова функции, они взаимозаменяемы как аргументы шаблонов, когда соответствующий параметр появляется только в контекстах вызова функции.operator()
Особенно распространенным типом функтора является предикат . Например, такие алгоритмы, как find_if
принимают унарный предикат, который работает с элементами последовательности. Такие алгоритмы, как sort, partial_sort, nth_element и все сортированные контейнеры, используют бинарный предикат, который должен обеспечивать строгое слабое упорядочение , то есть он должен вести себя как проверка принадлежности к транзитивному, нерефлексивному и асимметричному бинарному отношению . Если ничего не указано, эти алгоритмы и контейнеры по умолчанию используют less, который, в свою очередь, вызывает оператор «меньше чем» <.
Качество реализации (QoI) компилятора C++ оказывает большое влияние на удобство использования STL (и шаблонного кода в целом):
copy_if
был исключен [12] , хотя он был добавлен в C++11 . [13]STL состоит из
контейнеров
,
итераторов
,
объектов функций
и
алгоритмов.
Большинство алгоритмических шаблонов библиотеки, которые работают со структурами данных, имеют интерфейсы, которые используют диапазоны. Диапазон — это пара итераторов, которые обозначают начало и конец вычисления. [...] в общем случае диапазон [i, j) относится к элементам в структуре данных, начиная с того, на который указывает i, и до того, на который указывает j, но не включая его. Диапазон [i, j) действителен тогда и только тогда, когда j достижим из i.