В чем разница между std::vector
и std::array
в С++? Когда следует отдать предпочтение другому? Каковы плюсы и минусы каждого? Весь мой учебник показывает, как они одинаковы.
Std::vector против std:: array в С++
Ответ 1
std::vector
- это класс шаблонов, который инкапсулирует динамический массив 1 хранящийся в куче, который растет и сжимается автоматически, если элементы добавляются или удаляются. Он предоставляет все крючки (begin()
, end()
, итераторы и т.д.), Которые делают его работу с остальной частью STL. Он также имеет несколько полезных методов, которые позволяют выполнять операции, которые на обычном массиве будут громоздкими, например, например. вставляя элементы в середине вектора (он обрабатывает всю работу по перемещению следующих элементов за кулисами).
Поскольку он хранит элементы в памяти, выделенные в куче, он имеет некоторые накладные расходы в отношении статических массивов.
std::array
- это шаблонный класс, который инкапсулирует массив статического размера, который хранится внутри самого объекта, а это означает, что если вы создаете экземпляр класса в стеке, сам массив будет находиться в стеке. Его размер должен быть известен во время компиляции (он передавался как параметр шаблона), и он не может расти или сокращаться.
Он более ограничен, чем std::vector
, но он часто более эффективен, особенно для небольших размеров, потому что на практике это в основном облегченная оболочка вокруг массива C-стиля. Тем не менее, он более безопасен, поскольку неявное преобразование в указатель отключено, и оно обеспечивает большую часть связанных с STL функций std::vector
и других контейнеров, поэтому вы можете легко использовать его с алгоритмами STL и co. Во всяком случае, для самого ограничения фиксированного размера он гораздо менее гибкий, чем std::vector
.
Для введения в std::array
, посмотрите эту статью; для быстрого ознакомления с std::vector
и с возможными операциями, вы можете посмотреть документацию .
-
На самом деле, я думаю, что в стандарте они описываются с точки зрения максимальной сложности различных операций (например, случайный доступ в постоянное время, итерация по всем элементам в линейном времени, добавление и удаление элементов на конец в постоянном амортизированном времени и т.д.), но AFAIK нет другого метода выполнения таких требований, кроме использования динамического массива.Как указано в @Lucretiel, стандарт фактически требует, чтобы элементы хранились соприкасаемо, так что это динамический массив, который хранится там, где его назначает соответствующий распределитель.
Ответ 2
Использование класса std::vector<T>
:
-
... так же быстро, как и встроенные массивы, предполагая, что вы делаете только то, что позволяют встроенные массивы (чтение и запись в существующие элементы).
-
... автоматически изменяет размер при вставке новых элементов.
-
... позволяет вставлять новые элементы в начале или в середине вектора, автоматически "перемещая" остальные элементы "вверх" (это имеет смысл?). Он позволяет удалять элементы в любом месте
std::vector
, также автоматически перемещая остальные элементы вниз. -
... позволяет выполнить проверенное диапазоном чтение с помощью метода
at()
(вы всегда можете использовать индексы[]
, если вы не хотите, чтобы эта проверка выполнялась).
В std::vector<T>
std::vector<T>
есть два:
-
У вас нет надежного доступа к базовому указателю, что может быть проблемой, если вы имеете дело со сторонними функциями, требующими адрес массива.
-
Класс
std::vector<bool>
глупо. Он реализован как сжатое битовое поле, а не как массив. Избегайте его, если вам нужен массивbool
s! -
Во время использования
std::vector<T>
будет немного больше, чем массив С++ с таким же количеством элементов. Это связано с тем, что им необходимо отслеживать небольшую часть другой информации, такой как их текущий размер, и потому, что когда размерstd::vector<T>
изменяется, они оставляют больше места, чем нужно. Это делается для того, чтобы не изменять их каждый раз при вставке нового элемента. Такое поведение можно изменить, предоставив пользовательскийallocator
, но я никогда не чувствовал необходимости делать это!
Изменить: прочитав ответ Зуда на вопрос, я почувствовал, что должен добавить это:
Класс std::array<T>
не совпадает с массивом С++. std::array<T>
- очень тонкая оболочка вокруг массивов С++, с основной целью скрытия указателя от пользователя класса (в С++ массивы неявно отображаются в качестве указателей, что часто приводит к ужасающему эффекту). Класс std::array<T>
также сохраняет свой размер (длину), что может быть очень полезно.
Ответ 3
Чтобы подчеркнуть точку, сделанную @MatteoItalia, разница в эффективности заключается в том, где хранятся данные. Память кучи (требуется с vector
) требует вызова системы для выделения памяти, и это может быть дорого, если вы считаете циклы. Память стека (возможная для array
) практически равна нулю с точки зрения времени, поскольку память распределяется путем простой настройки указателя стека, и она выполняется только один раз при входе в функцию. Стек также предотвращает фрагментацию памяти. Разумеется, std::array
не всегда будет в стеке; это зависит от того, где вы его выделяете, но все равно будет включать в себя еще одно распределение памяти из кучи по сравнению с вектором. Если у вас есть
- small "array" (менее 100 элементов) - (типичный стек составляет около 8 МБ, поэтому не выделяйте более нескольких килобайт в стеке или меньше, если ваш код рекурсивный)
- размер будет исправлен.
- время жизни находится в области функций (или является значением члена с тем же временем жизни, что и родительский класс)
- вы считаете циклы,
определенно используйте std::array
над вектором. Если какое-либо из этих требований неверно, используйте std::vector
.
Ответ 4
Если вы планируете использовать многомерные массивы, то есть одно дополнительное различие между std:: array и std::vector. Многомерный std:: array будет иметь элементы, упакованные в память во всех измерениях, так же, как и в стилевом массиве style. Многомерный 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 и заставить его работать из.
Ответ 5
Одно из преимуществ, которое векторы имеют над массивами, состоит в том, что можно найти текущий размер вектора, используя vector_name.size().
Как вы можете себе представить, это может быть весьма полезно в самых разных ситуациях, где вы можете легко найти количество элементов в массиве.
Ответ 6
Вектор - это контейнерный класс, а массив - выделенная память.