Есть хорошо известное изображение (шпаргалка) под названием «Выбор контейнера C ++». Это блок-схема, чтобы выбрать лучший контейнер для желаемого использования.
Кто-нибудь знает, есть ли уже версия на C ++ 11?
Это предыдущий:
Есть хорошо известное изображение (шпаргалка) под названием «Выбор контейнера C ++». Это блок-схема, чтобы выбрать лучший контейнер для желаемого использования.
Кто-нибудь знает, есть ли уже версия на C ++ 11?
Это предыдущий:
Ответы:
Не то, чтобы я знал, однако это может быть сделано в тексте, я думаю. Кроме того, график немного смещен, потому что list
он не такой хороший контейнер в целом и не является таковым forward_list
. Оба списка являются очень специализированными контейнерами для нишевых приложений.
Чтобы построить такую диаграмму, вам просто нужно два простых правила:
Вначале беспокоиться о производительности обычно бесполезно. Соображения, касающиеся больших О, действительно начинают действовать, когда вы начинаете обрабатывать несколько тысяч (или более) предметов.
Есть две большие категории контейнеров:
find
операцияа затем вы можете создать несколько адаптеров на них: stack
, queue
, priority_queue
. Я оставлю здесь адаптеры, они достаточно специализированы, чтобы их можно было узнать.
Вопрос 1: Ассоциативный ?
Вопрос 1.1: Заказал ?
unordered_
контейнер, в противном случае используйте его традиционный заказанный аналог.Вопрос 1.2: Отдельный ключ ?
map
, в противном случае используйте aset
Вопрос 1.3: Дубликаты ?
multi
, иначе - нет.Пример:
Предположим, что у меня есть несколько человек с уникальным идентификатором, связанным с ними, и я хотел бы получить данные о человеке из его идентификатора как можно проще.
Я хочу find
функцию, таким образом, ассоциативный контейнер
1.1. Я не мог заботиться о заказе, поэтому unordered_
контейнер
1.2. Мой ключ (ID) отделен от значения, с которым он связан, таким образом,map
1.3. Идентификатор уникален, поэтому дубликат не должен закрадываться.
Окончательный ответ: std::unordered_map<ID, PersonData>
.
Вопрос 2: память стабильна ?
list
Вопрос 2.1: который ?
list
; a forward_list
полезен только для уменьшения занимаемой памяти.Вопрос 3: Динамический размер ?
{ ... }
синтаксис), тогда используйте array
, Он заменяет традиционный C-массив, но с удобными функциями.Вопрос 4: двусторонний ?
deque
, в противном случае используйте a vector
.Вы заметите, что по умолчанию, если вам не нужен ассоциативный контейнер, ваш выбор будет a vector
. Оказывается, это также рекомендация Саттера и Страуструпа .
array
не требует конструируемого типа по умолчанию; 2) выбор multi
s не столько о разрешении дубликатов, сколько о сохранении их значения (вы можете поместить дубликаты в не- multi
контейнеры, просто бывает, что хранится только один).
map.find(key)
гораздо приятнее на вкус, чем std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));
, поэтому, семантически важно, чтобы find
это была функция-член, а не функция из нее <algorithm>
. Что касается O (1) против O (log n), это не влияет на семантику; Я удалю «эффективно» из примера и заменю на «легко».
deque
было ли это свойство тоже?
deque
элементы стабильны, только если вы нажмете / поп с любого конца; если вы начнете вставлять / стирать в середине, то до N / 2 элементов будут перетасовываться, чтобы заполнить созданный промежуток.
Мне нравится ответ Матье, но я собираюсь пересказать блок-схему как это:
По умолчанию, если вам нужен контейнер с вещами, используйте std::vector
. Таким образом, любой другой контейнер оправдывается только предоставлением некоторой функциональной альтернативы std::vector
.
std::vector
требует, чтобы его содержимое было пригодно для перемещения, поскольку оно должно иметь возможность перетасовывать предметы вокруг. Это не страшное бремя для содержимого (обратите внимание, что конструкторы по умолчанию не требуются , emplace
и так далее). Однако большинство других контейнеров не требуют какого-либо конкретного конструктора (опять же, спасибо emplace
). Поэтому, если у вас есть объект, в котором вы абсолютно не можете реализовать конструктор перемещения, вам придется выбрать что-то другое.
A std::deque
будет общей заменой, имеющей многие свойства std::vector
, но вы можете вставить ее только на обоих концах deque. Вставки посередине требуют перемещения. А std::list
не предъявляет никаких требований к его содержанию.
std::vector<bool>
не является. Ну, это стандартно. Но это не vector
в обычном смысле, поскольку операции, которые std::vector
обычно разрешены, запрещены. И это, безусловно , не содержит bool
с .
Поэтому, если вам нужно реальное vector
поведение из контейнера bool
s, вы не получите его std::vector<bool>
. Так что вам придется сделать должным с std::deque<bool>
.
Если вам нужно найти элементы в контейнере, а поисковый тег не может быть просто индексом, то вам, возможно, придется отказаться std::vector
в пользу set
и map
. Обратите внимание на ключевое слово « может »; Сортировка std::vector
иногда является разумной альтернативой. Или Boost.Container 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
производительность вставки может быть не такой плохой, как раньше. В некоторых реализациях реализована форма копирования элементов на основе семантики перемещения (так называемая «swaptimization»), но теперь, когда перемещение является частью языка, оно предписано стандартом.
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
!
vector<bool>
является vector<char>
.
std::allocator<T>
не поддерживает это выравнивание (и я не знаю, почему это не так), то вы всегда можете использовать свой собственный распределитель.
std::vector::resize
имеет перегрузку, которая не принимает значения (она просто принимает новый размер; любые новые элементы будут создаваться по умолчанию на месте). Кроме того, почему компиляторы не могут правильно выровнять параметры-значения, даже если они объявлены с таким выравниванием?
bitset
для bool, если вы заранее знаете размер en.cppreference.com/w/cpp/utility/bitset
Вот версия C ++ 11 вышеупомянутой блок-схемы. [первоначально опубликовано без указания авторства, Микаэль Перссон ]
Вот быстрое вращение, хотя это, вероятно, нуждается в работе
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 версии, в первую очередь в связи с тем , что я действительно не люблю связанные узлы. Связанные узлы-контейнеры обычно могут работать быстрее, чем несвязанные контейнеры, за исключением нескольких редких ситуаций. Если вы не знаете, что это за ситуации, и не имеете доступа к boost, не используйте контейнеры связанных узлов. (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Этот список фокусируется в основном на небольших и средних контейнерах, потому что (A) это 99,99% от того, с чем мы имеем дело в коде, и (B) Большому количеству элементов нужны собственные алгоритмы, а не разные контейнеры.