std :: vector против std :: array в C ++


283

В чем разница между a std::vectorи a std::arrayв C ++? Когда одно должно быть предпочтительнее другого? Каковы плюсы и минусы каждого? Все, что делает мой учебник, это перечисляет, как они одинаковы.


1
Я ищу сравнение std::vectorпротив std::arrayи как термины отличаются.
Зуд

Zud, std::arrayэто не то же самое, что массив C ++. std::arrayочень тонкая оболочка для массивов C ++, основная цель которой - скрыть указатель от пользователя класса. Я обновлю свой ответ.
ClosureCowboy

Я обновил название вопроса и текст, чтобы отразить ваши разъяснения.
Matteo Italia

Ответы:


318

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

Поскольку он хранит элементы в памяти, выделенной в куче, он имеет некоторые накладные расходы в отношении статических массивов.

std::arrayэто шаблонный класс, который инкапсулирует массив статического размера, хранящийся внутри самого объекта, что означает, что, если вы создадите экземпляр класса в стеке, сам массив будет в стеке. Его размер должен быть известен во время компиляции (он передается как параметр шаблона), и он не может увеличиваться или уменьшаться.

Он более ограничен std::vector, но часто более эффективен, особенно для небольших размеров, потому что на практике это в основном облегченная оболочка вокруг массива в стиле C. Однако он более безопасен, поскольку неявное преобразование в указатель отключено и обеспечивает большую часть функций, связанных с 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 бозон

3
@ Zboson: это определенно не только ты, статика - довольно злоупотребленный термин; само staticключевое слово в C ++ имеет три разных значения, и этот термин также часто используется, чтобы говорить о вещах, которые исправлены во время компиляции. Я надеюсь, что «статический размер» немного более понятен.
Matteo Italia

3
Одно замечание: в режиме реального времени программирования (где вы не должны иметь какое - либо динамическое распределение / открепление после запуска) Std :: массива, вероятно , будет предпочтительнее станд :: вектор.
TED

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>размер s изменяется, они резервируют больше места, чем им нужно. Это предотвращает изменение размера при каждом добавлении нового элемента. Такое поведение можно изменить, предоставив кастом allocator, но я никогда не чувствовал необходимости делать это!


Изменить: После прочтения ответа Zud на вопрос, я чувствовал, что я должен добавить это:

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


5
Это «так же быстро», как и использование динамически размещаемого встроенного массива. С другой стороны, использование автоматического массива может значительно отличаться по производительности (и не только во время распределения из-за эффектов локальности).
Бен Фойгт,

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

16

Чтобы подчеркнуть точку зрения @MatteoItalia, разница в эффективности заключается в том, где хранятся данные. Куча памяти (требуется с vector) требует вызова системы для выделения памяти, и это может быть дорого, если вы считаете циклы. Стековая память (возможная для array) практически не требует затрат времени, потому что память выделяется путем простой настройки указателя стека, и это делается один раз при входе в функцию. Стек также позволяет избежать фрагментации памяти. Конечно, std::arrayне всегда будет в стеке; это зависит от того, где вы его выделите, но все равно будет выделяться на одну кучу меньше памяти из кучи по сравнению с вектором. Если у тебя есть

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

определенно используйте std::arrayнад вектором. Если какое-либо из этих требований не соответствует действительности, используйте a std::vector.


3
Хороший ответ. «Чтобы быть уверенным, std :: array не всегда будет в стеке; это зависит от того, где вы его разместите». Так как я могу создать std :: array не в стеке с большим количеством элементов?
Триларион

4
Используйте @Trilarion new std::arrayили сделайте его членом класса, который вы используете 'new` для распределения.
Марк Лаката

Таким образом, это означает, что new std::arrayвсе еще ожидает знать его размер во время компиляции и не может изменить его размер, но все еще живет в куче?
Триларион

Да. Там нет значительного преимущества использования new std::arrayпротив new std::vector.
Марк Лаката

12

Если вы рассматриваете возможность использования многомерных массивов, то есть одно дополнительное отличие между std :: array и std :: vector. У многомерного std :: array будут элементы, упакованные в память во всех измерениях, так же, как массив AC стиля. Многомерный std :: vector не будет упакован во всех измерениях.

Учитывая следующие декларации:

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 :: array в openGL и заставить его работать.


-17

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


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