Есть известное изображение (шпаргалка) под названием "Выбор контейнера С++". Это блок-схема выбора наилучшего контейнера для желаемого использования.
Кто-нибудь знает, есть ли уже его версия для С++ 11?
Это предыдущий:
Есть известное изображение (шпаргалка) под названием "Выбор контейнера С++". Это блок-схема выбора наилучшего контейнера для желаемого использования.
Кто-нибудь знает, есть ли уже его версия для С++ 11?
Это предыдущий:
Не то, чтобы я знал, однако это можно сделать в текстовом виде, я думаю. Кроме того, диаграмма немного неверна, потому что list
в целом не такой уж хороший контейнер, как и forward_list
. Оба списка представляют собой очень специализированные контейнеры для нишевых приложений.
Чтобы построить такую диаграмму, вам просто нужны две простые рекомендации:
Беспокоиться о производительности поначалу обычно бесполезно. Соображения большого O действительно начинают действовать только тогда, когда вы начинаете обрабатывать несколько тысяч (или более) элементов.
Есть две большие категории контейнеров:
find
а потом поверх них можно построить несколько адаптеров: stack
, queue
, priority_queue
. Я оставлю здесь адаптеры, они достаточно специализированы, чтобы быть узнаваемыми.
Вопрос 1: Ассоциативный?
Вопрос 1.1: Заказано?
unordered_
, в противном случае используйте его традиционный упорядоченный аналог.Вопрос 1.2: Отдельный ключ?
map
, в противном случае используйте set
.Вопрос 1.3: Дубликаты?
multi
, иначе не делайте этого.Пример:
Предположим, что у меня есть несколько человек с уникальным идентификатором, связанным с ними, и я хотел бы получить данные о человеке из его идентификатора как можно проще.
find
, то есть ассоциативный контейнер1.1. Мне наплевать на порядок, поэтому контейнер unordered_
1.2. Мой ключ (ID) отделен от значения, с которым он связан, поэтому map
1.3. Идентификатор уникален, поэтому не должно быть дубликатов.
Окончательный ответ: std::unordered_map<ID, PersonData>
.
Вопрос 2: Стабильная память?
list
Вопрос 2.1: Какой?
list
; forward_list
полезен только для меньшего объема памяти.Вопрос 3: Динамический размер?
{ ... }
), а затем использовать array
. Он заменяет традиционный C-массив, но с удобными функциями.Вопрос 4: Двусторонний?
deque
, в противном случае используйте vector
.Вы заметите, что по умолчанию, если вам не нужен ассоциативный контейнер, ваш выбор будет vector
. Оказывается, это также рекомендации Саттера и Страуструпа.
array
не требует конструктивного типа по умолчанию; 2) выбор multi
заключается не столько в том, что дубликаты разрешены, сколько в том, имеет ли значение их сохранение (вы можете поместить дубликаты в контейнеры, отличные от multi
, просто бывает, что только один сохраняется).
- person R. Martinho Fernandes; 22.05.2012
map.find(key)
гораздо приятнее, чем std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));
, поэтому семантически важно, чтобы find
была функцией-членом, а не функцией из <algorithm>
. Что касается O(1) и O(log n), это не влияет на семантику; Я уберу эффективно из примера и заменю его на легко.
- person Matthieu M.; 22.05.2012
deque
тоже имеет это свойство?
- person Martin Ba; 05.07.2013
deque
элементы стабильны только, если вы толкаете/отталкиваете с любого конца; если вы начинаете вставлять/стирать в середине, то до N/2 элементов перемешиваются, чтобы заполнить созданный пробел.
- person Matthieu M.; 05.07.2013
Мне нравится ответ Матье, но я собираюсь переформулировать блок-схему следующим образом:
По умолчанию, если вам нужен контейнер с вещами, используйте std::vector
. Таким образом, любой другой контейнер оправдан только предоставлением некоторой функциональности, альтернативной std::vector
.
std::vector
требует, чтобы его содержимое можно было перемещать, так как он должен иметь возможность перетасовывать элементы. Это не страшное бремя для содержимого (обратите внимание, что конструкторы по умолчанию не требуются, благодаря emplace
и т. д.). Однако большинству других контейнеров не требуется какой-либо конкретный конструктор (опять же, благодаря emplace
). Поэтому, если у вас есть объект, в котором вы абсолютно не можете реализовать конструктор перемещения, вам придется выбрать что-то другое.
std::deque
будет общей заменой, имеющей многие свойства std::vector
, но вы можете вставлять только на обоих концах двухсторонней очереди. Вставки посередине требуют перемещения. std::list
не предъявляет никаких требований к своему содержимому.
std::vector<bool>
это... нет. Ну это стандартно. Но это не vector
в обычном смысле, так как операции, которые std::vector
обычно разрешены, запрещены. И уж точно не содержит bool
s.
Следовательно, если вам нужно настоящее vector
поведение из контейнера bool
, вы не получите его от std::vector<bool>
. Так что вам придется заплатить std::deque<bool>
.
Если вам нужно найти элементы в контейнере, а тег поиска не может быть просто индексом, то вам может понадобиться отказаться от std::vector
в пользу set
и map
. Обратите внимание на ключевое слово "может"; отсортированный std::vector
иногда является разумной альтернативой. Или flat_set/map
, который реализует отсортированный std::vector
.
В настоящее время существует четыре их варианта, каждый со своими потребностями.
map
, если тег поиска не совпадает с самим элементом, который вы ищете. В противном случае используйте set
.unordered
, если у вас много элементов в контейнере и производительность поиска обязательно должна быть O(1)
, а не O(logn)
.multi
, если вам нужно, чтобы несколько элементов имели один и тот же тег поиска.Если вам нужно, чтобы контейнер элементов всегда сортировался на основе конкретной операции сравнения, вы можете использовать set
. Или multi_set
, если вам нужно, чтобы несколько элементов имели одинаковое значение.
Или вы можете использовать отсортированный std::vector
, но вам придется держать его в сортированном виде.
Когда итераторы и ссылки становятся недействительными, иногда возникает проблема. Если вам нужен список элементов, так что у вас есть итераторы/указатели на эти элементы в других местах, то подход std::vector
к аннулированию может быть неподходящим. Любая операция вставки может привести к аннулированию в зависимости от текущего размера и емкости.
std::list
предлагает твердую гарантию: итератор и связанные с ним ссылки/указатели становятся недействительными только тогда, когда сам элемент удаляется из контейнера. std::forward_list
есть, если память вызывает серьезную озабоченность.
Если это слишком сильная гарантия, std::deque
предлагает более слабую, но полезную гарантию. Аннулирование происходит из-за вставок в середине, но вставки в начало или конец приводят к аннулированию только итераторов, а не указателей/ссылок на элементы в контейнере.
std::vector
обеспечивает дешевую вставку только в конце (да и то становится дорого, если продуть емкость).
std::list
дорого с точки зрения производительности (каждый новый вставленный элемент требует выделения памяти), но он непротиворечив. Он также предлагает иногда незаменимую возможность перетасовывать предметы практически без затрат на производительность, а также обменивать предметы с другими std::list
контейнерами того же типа без потери производительности. Если вам нужно много перетасовать вещи, используйте std::list
.
std::deque
обеспечивает вставку/удаление с постоянным временем в начале и в конце, но вставка в середине может быть довольно дорогой. Так что, если вам нужно добавить/удалить что-то как спереди, так и сзади, std::deque
может быть тем, что вам нужно.
Следует отметить, что благодаря семантике перемещения производительность вставки std::vector
может быть не такой плохой, как раньше. В некоторых реализациях была реализована форма копирования элемента на основе семантики перемещения (так называемая «обмен подкачками»), но теперь, когда перемещение является частью языка, оно предписано стандартом.
std::array
— прекрасный контейнер, если вам нужно как можно меньше динамических выделений. Это просто оболочка вокруг C-массива; это означает, что его размер должен быть известен во время во время компиляции. Если вы можете жить с этим, используйте std::array
.
При этом использование std::vector
и reserve
размера будет работать так же хорошо для ограниченного std::vector
. Таким образом, фактический размер может варьироваться, и вы получаете только одно выделение памяти (если только вы не убьете емкость).
std::sort
, есть еще std::inplace_merge
, который интересно легко размещать новые элементы (а не вызов std::lower_bound
+ std::vector::insert
). Приятно узнать о flat_set
и flat_map
!
- person Matthieu M.; 23.05.2012
vector<bool>
является vector<char>
.
- person Inverse; 26.05.2012
std::allocator<T>
не поддерживает это выравнивание (и я не знаю, почему бы и нет), вы всегда можете использовать свой собственный распределитель.
- person Nicol Bolas; 26.05.2012
std::vector::resize
принимает свой тип по значению, см. stackoverflow.com/a/2340728/158259
- person Inverse; 27.05.2012
std::vector::resize
имеет перегрузку, которая не принять значение (он просто принимает новый размер; любые новые элементы будут создаваться по умолчанию на месте). Кроме того, почему компиляторы не могут правильно выровнять параметры значений, даже если они объявлены как имеющие такое выравнивание?
- person Nicol Bolas; 27.05.2012
bitset
для bool, если вы заранее знаете размер en.cppreference.com/w/cpp/ утилита/битсет
- person bendervader; 02.07.2015
bitset
ничего не делает для устранения предупреждений этого сообщения о vector<bool>
, учитывая, что у него те же проблемы: на самом деле он не содержит набор экземпляров bool
, а скорее упакованные биты, доступ к которым осуществляется через экземпляры прокси-класса, возвращаемые по значению. В контексте этого вопроса его единственная сила по сравнению с vector<bool>
заключается в том, что он не обладает безнадежно вводящим в заблуждение именем и бесконечным презрением со стороны всех, кто связан с языком. Кстати, вместо того, чтобы сдаться и использовать deque
для получения реального контейнера bool
, мы можем довольно легко создать тонкую структуру-оболочку вокруг экземпляра bool
.
- person underscore_d; 11.09.2016
Вот версия C++11 приведенной выше блок-схемы. [первоначально опубликовано без указания авторства, Mikael Persson]
Вот быстрое вращение, хотя, вероятно, оно нуждается в доработке
Should the container let you manage the order of the elements?
Yes:
Will the container contain always exactly the same number of elements?
Yes:
Does the container need a fast move operator?
Yes: std::vector
No: std::array
No:
Do you absolutely need stable iterators? (be certain!)
Yes: boost::stable_vector (as a last case fallback, std::list)
No:
Do inserts happen only at the ends?
Yes: std::deque
No: std::vector
No:
Are keys associated with Values?
Yes:
Do the keys need to be sorted?
Yes:
Are there more than one value per key?
Yes: boost::flat_map (as a last case fallback, std::map)
No: boost::flat_multimap (as a last case fallback, std::map)
No:
Are there more than one value per key?
Yes: std::unordered_multimap
No: std::unordered_map
No:
Are elements read then removed in a certain order?
Yes:
Order is:
Ordered by element: std::priority_queue
First in First out: std::queue
First in Last out: std::stack
Other: Custom based on std::vector?????
No:
Should the elements be sorted by value?
Yes: boost::flat_set
No: std::vector
Вы можете заметить, что это сильно отличается от версии C++03, прежде всего из-за того, что я очень не люблю связанные узлы. Контейнеры связанных узлов обычно могут быть лучше несвязанных контейнеров по производительности, за исключением нескольких редких ситуаций. Если вы не знаете, что это за ситуации, и у вас есть доступ к ускорению, не используйте контейнеры связанных узлов. (std::list, std::slist, std::map, std::multimap, std::set, std::multiset). Этот список в основном сосредоточен на малых и средних контейнерах, потому что (А) это 99,99% того, с чем мы имеем дело в коде, и (Б) для большого количества элементов нужны специальные алгоритмы, а не разные контейнеры.
forward_list
иarray
. И неупорядоченные контейнеры на самом деле не запутали бы его; они просто добавят новый кластер, связанный с Need to find element by key. - person Mike Seymour   schedule 22.05.2012std::vector
для упорядоченных контейнеров, сохраняя сортировку данных самостоятельно. - person James Kanze   schedule 22.05.2012