std :: вектор проти std :: масив на C ++


283

У чому різниця між a std::vectorі std::arrayin у C ++? Коли слід віддавати перевагу іншому? Які плюси і мінуси у кожного? У моєму підручнику перелічено, як вони однакові.


1
Я шукаю порівняння та std::vectorпорівняння std::arrayтермінів.
Зуд

Zud, std::arrayне те саме, що масив C ++. std::arrayявляє собою дуже тонку обгортку навколо масивів C ++, основна мета якої - приховати покажчик від користувача класу. Я оновлю свою відповідь.
ЗакриттяКовбой

Я оновив назву питання та текст, щоб відобразити ваше уточнення.
Маттео Італія

Відповіді:


318

std::vector- клас шаблонів, який інкапсулює динамічний масив 1 , що зберігається в купі, який росте і скорочується автоматично, якщо елементи додаються або видаляються. Він надає всі гачки ( begin(), end()ітератори, і т.д.) , які роблять його роботу відмінно з іншою частиною STL. У ньому також є кілька корисних методів, які дозволяють виконувати операції, які в нормальному масиві були б громіздкими, як, наприклад, вставлення елементів в середину вектора (він обробляє всю роботу з переміщення наступних елементів за кадром).

Оскільки він зберігає елементи в пам'яті, виділеній на купі, він має деякі накладні витрати щодо статичних масивів.

std::array- клас шаблонів, який інкапсулює масив статичного розміру, що зберігається всередині самого об'єкта, а це означає, що, якщо інстанціювати клас у стеці, сам масив опиниться у стеку. Його розмір повинен бути відомий під час компіляції (він передається як параметр шаблону), і він не може зростати або зменшуватися.

Це більш обмежено std::vector, але часто ефективніше, особливо для невеликих розмірів, оскільки на практиці це в основному легка обгортка навколо масиву стилю С. Однак це більш безпечно, оскільки неявна конверсія в покажчик вимкнена, і вона забезпечує більшу частину функцій, пов'язаних з STL, std::vectorта інших контейнерів, тому ви можете легко використовувати його з алгоритмами STL & co. У всякому разі, для самого обмеження фіксованого розміру він набагато менш гнучкий, ніж std::vector.

Для ознайомлення std::arrayознайомтеся з цією статтею ; для швидкого ознайомлення std::vectorз операціями, які можливі на ньому, ви можете переглянути його документацію .


  1. Власне, я думаю, що в стандарті вони описуються з точки зору максимальної складності різних операцій (наприклад, випадковий доступ у постійному часі, ітерація над усіма елементами в лінійний час, додавання та видалення елементів наприкінці у постійному амортизованому часі, тощо), але AFAIK не існує іншого способу виконання таких вимог, крім використання динамічного масиву. Як зазначає @Lucretiel, стандарт фактично вимагає, щоб елементи зберігалися безперервно, тому це динамічний масив, який зберігається там, де його ставить асоційований розподільник.

6
Щодо вашої виноски: Хоча це правда, стандарт також гарантує, що вказує арифметику на внутрішніх елементах, а це означає, що він повинен бути масивом: & vec [9] - & vec [3] == 6 є істинним.
Лукретьєль

9
Я майже впевнений, що вектор не зменшується автоматично, але оскільки C ++ 11 ви можете зателефонувати shrink_to_fit.
Діно

Мене зовсім бентежить термін статичний масив, і я не впевнений, що таке правильна термінологія. Ви маєте на увазі масив статичного розміру, а не масив статичної змінної (для одного використовується статичне сховище). stackoverflow.com/questions/2672085/… . Яка правильна термінологія? Чи статичний масив неохайний термін для масиву з фіксованим розміром?
Z boson

3
@Zboson: це точно не тільки ти, статичний - це досить зловживаний термін; саме staticключове слово в C ++ має три різні непов'язані значення, і цей термін також часто використовується для розмови про речі, які фіксуються під час компіляції. Я сподіваюся, що "статичного розміру" дещо чіткіше.
Маттео Італія

3
Варто зазначити одне: для програмування в режимі реального часу (де у вас не повинно бути жодного динамічного розподілу / розстановки після запуску) std :: array, мабуть, буде кращим над std :: vector.
ТЕД

17

Використання std::vector<T>класу:

  • ... так само швидко, як і використання вбудованих масивів, припускаючи, що ви робите лише ті вбудовані масиви, які дозволяють вам робити (читати та записувати в існуючі елементи).

  • ... автоматично змінюється розмір, коли вставляються нові елементи.

  • ... дозволяє вставляти нові елементи на початку або в середині вектора, автоматично "зміщуючи" решту елементів "вгору" (чи має це сенс?). Це дозволяє прибрати елементи де завгодно std::vector, також автоматично пересуваючи решту елементів вниз.

  • ... дозволяє виконувати перевірений діапазон зчитування at()методом (ви завжди можете використовувати індексатори, []якщо ви не хочете, щоб ця перевірка виконувалася).

Є два три основні застереження щодо використання std::vector<T>:

  1. Ви не маєте надійного доступу до основного вказівника, що може бути проблемою, якщо ви маєте справу зі сторонніми функціями, які вимагають адреси масиву.

  2. std::vector<bool>Клас нерозумно. Він реалізований як згущене бітполе, а не як масив. Уникайте цього, якщо хочете масив bools!

  3. Під час використання std::vector<T>s буде трохи більшим, ніж масив C ++ з однаковою кількістю елементів. Це тому, що їм потрібно відслідковувати невелику кількість іншої інформації, наприклад, їх поточний розмір, і тому що, коли std::vector<T>розмір змінюється, вони залишають більше місця, ніж їм потрібно. Це запобігає необхідності зміни розміру кожного разу, коли вставляється новий елемент. Таку поведінку можна змінити, надавши звичай allocator, але я ніколи не відчував потреби цього робити!


Редагувати: Прочитавши відповідь Зуда на запитання, я відчув, що я повинен додати це:

std::array<T>Клас не такий же , як масив C ++. std::array<T>являє собою дуже тонку обгортку навколо масивів C ++, основна мета якої - приховати вказівник від користувача класу (у C ++ масиви неявно виводяться як покажчики, найчастіше на ефект ураження). std::array<T>Клас також зберігає її розмір (довжина), яка може бути дуже корисним.


5
Це "так само швидко", як використання вбудованого масиву, що динамічно розподіляється. З іншого боку, використання автоматичного масиву може мати значно різну продуктивність (і не тільки під час розподілу через ефекти локальності).
Бен Войгт,

4
Для векторів, що не data()стосуються булів, в C ++ 11 і пізніших версіях ви можете зателефонувати на a, std::vector<T>щоб отримати нижній вказівник. Ви також можете просто взяти адресу елемента 0 (гарантовано, що буде працювати з C ++ 11, можливо, буде працювати з більш ранніми версіями).
Метт

16

Щоб підкреслити пункт, зроблений @MatteoItalia, різниця в ефективності полягає в тому, де зберігаються дані. Купа пам’яті (необхідна з vector) вимагає дзвінка в систему для розподілу пам'яті, і це може бути дорого, якщо ви рахуєте цикли. Пам'ять стека (можлива для array) практично "нульова" з точки зору часу, оскільки пам'ять виділяється шляхом простого налаштування покажчика стека, і це робиться лише один раз при вході в функцію. Стек також уникає фрагментації пам'яті. Безумовно, std::arrayне завжди буде в стеці; це залежить від того, куди ви його виділите, але він все ще буде мати один менший розподіл пам'яті з купи порівняно з вектором. Якщо у вас є

  • невеликий "масив" (під 100 елементами кажуть) - (типовий стек становить близько 8 МБ, тому не виділяйте на стеку більше кількох КБ або менше, якщо ваш код рекурсивний)
  • розмір буде фіксований
  • час життя знаходиться в області функцій (або є значенням члена з тим самим терміном служби, що і батьківський клас)
  • ви рахуєте цикли,

обов'язково використовуйте std::arrayнад вектор. Якщо будь-яка з цих вимог не відповідає дійсності, тоді використовуйте a std::vector.


3
Гарна відповідь. "Щоб бути впевненим, std :: масив не завжди буде в стеку; це залежить від місця його виділення". Як я можу створити std :: масив не на стеці з великою кількістю елементів?
Триларіон

4
@Trilarion використовувати new std::arrayабо зробити його членом класу, який ви використовуєте "new" для виділення.
Марк Лаката

Тож це означає, що new std::arrayвсе-таки розраховує дізнатися його розмір під час компіляції і не може змінити його розмір, але все ще живе на купі?
Триларіон

Так. Існує не значну перевагу у використанні new std::arrayпроти new std::vector.
Марк Лаката

12

Якщо ви розглядаєте можливість використання багатовимірних масивів, то існує одна додаткова різниця між std :: array та std :: vector. Багатовимірний масив std :: матиме елементи, запаковані в пам'ять у всіх вимірах, подібно до масиву змінного типу. Багатовимірний std :: вектор не буде упакований у всі виміри.

З огляду на такі декларації:

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

Вказівник на перший елемент у масиві c-style (cConc) або std :: array (aConc) може бути перетворений через увесь масив, додавши 1 до кожного попереднього елемента. Вони щільно упаковані.

Вказівник на перший елемент у векторному масиві (vConc) або вказівний масив (ptrConc) може бути повторений лише через перші 5 (у цьому випадку) елементів, і тоді є 12 байт (у моїй системі) накладних витрат наступний вектор.

Це означає, що масив std :: vector>, ініціалізований як [3] [1000] масив, буде значно меншим у пам'яті, ніж один, ініціалізований як [1000] [3] масив, і обидва будуть більшими в пам'яті, ніж std: масив, виділений будь-яким способом.

Це також означає, що ви не можете просто передати багатовимірний векторний (або вказівний) масив до, скажімо, openGL, не враховуючи накладні дані пам'яті, але ви можете наївно передавати багатовимірний std :: масив до openGL і допрацювати його.


-17

Вектор - клас контейнера, а масив - виділена пам'ять.


23
Здається, ваша відповідь спрямована std::vector<T>проти T[], але питання про std::vector<T>проти std::array<T>.
Кіт Пінсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.