Є добре відоме зображення (шпаргалка) під назвою "C ++ Container choice". Це схема вибору найкращого контейнера для потрібного використання.
Хтось знає, чи є вже версія C ++ 11?
Це попередній:
Є добре відоме зображення (шпаргалка) під назвою "C ++ Container choice". Це схема вибору найкращого контейнера для потрібного використання.
Хтось знає, чи є вже версія 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) окремо від значення, з яким він асоціюється, таким чином amap
1.3. Ідентифікатор унікальний, тому жоден дублікат не повинен повзати.
Відповідь: std::unordered_map<ID, PersonData>
.
Питання 2: Стабільна пам'ять ?
list
Питання 2.1: Що ?
list
; a forward_list
корисний лише для меншої площі пам'яті.Питання 3: Динамічно розмір ?
{ ... }
синтаксис), тоді використовуйте array
. Він замінює традиційний C-масив, але зручними функціями.Питання 4: Подвійний ?
deque
, інакше використовуйте vector
.Ви помітите , що, за замовчуванням, якщо не потрібен асоціативний контейнер, ваш вибір буде 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) vs O (log n), то це не впливає на семантику; Я приберу "ефективно" з прикладу і заміню його на "легко".
deque
має і ця властивість?
deque
елементі елементи стабільні лише у тому випадку, якщо ви натискаєте / стискаєте на будь-якому кінці; якщо ви почнете вставляти / стирати в середині, то до N / 2 елементів змішуються, щоб заповнити створений пробіл.
Мені подобається відповідь Матьє, але я збираюсь переробити блок-схему так:
За замовчуванням, якщо вам потрібен контейнер з речами, використовуйте std::vector
. Таким чином, кожен інший контейнер виправданий лише наданням певної функціональної альтернативи std::vector
.
std::vector
вимагає, щоб його вміст було зручним для переміщення, оскільки йому потрібно вміти переміщувати елементи навколо. Це не є страшним тягарем для вмісту (зауважте, що конструктори за замовчуванням не потрібні , завдяки emplace
тощо). Однак для більшості інших контейнерів не потрібен конкретний конструктор (знову ж таки, завдяки emplace
). Тож якщо у вас є об'єкт, де ви абсолютно не можете реалізувати конструктор переміщення, вам доведеться вибрати щось інше.
A std::deque
була б загальною заміною, яка має багато властивостей std::vector
, але вставляти їх можна лише на обох кінцях деки. Вставки в середині вимагають переміщення. Місце std::list
не пред'являє жодних вимог до його змісту.
std::vector<bool>
не. Ну, це стандартно. Але це не vector
у звичному розумінні, оскільки операції, які std::vector
зазвичай дозволяють, заборонені. І це, безумовно , не містить bool
s .
Тому, якщо вам потрібна реальна vector
поведінка з контейнера bool
s, ви не збираєтесь її отримувати std::vector<bool>
. Тож вам доведеться погодитись із std::deque<bool>
.
Якщо вам потрібно знайти елементи в контейнері, а пошуковий тег не може бути просто індексом, можливо, вам доведеться відмовитися std::vector
на користь set
і map
. Зверніть увагу на ключове слово " може "; сортування std::vector
- іноді розумна альтернатива. Або Boost.Container's 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
- прекрасний контейнер, якщо ви хочете отримати найменші динамічні виділення. Це просто обгортка навколо масиву С; це означає, що його розмір повинен бути відомий під час компіляції . Якщо ви можете з цим жити, то використовуйте 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, насамперед через те, що я дійсно не люблю пов'язані вузли. З'єднані контейнери з вузлами зазвичай можуть бути обіграні по продуктивності не пов'язаним контейнером, за винятком кількох рідкісних ситуацій. Якщо ви не знаєте, що це за ситуації, і у вас є доступ до підвищення, не використовуйте пов'язані контейнери для вузлів. (std :: список, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Цей список зосереджується здебільшого на малих та середньосторонніх контейнерах, оскільки (А) це 99,99% від того, що ми маємо справу з кодом, і (В) Великій кількості елементів потрібні спеціальні алгоритми, а не різні контейнери.