В чем разница между a std::vectorи a std::arrayв C ++? Когда одно должно быть предпочтительнее другого? Каковы плюсы и минусы каждого? Все, что делает мой учебник, это перечисляет, как они одинаковы.
Я ищу сравнение 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операциями, которые возможны на нем, вы можете посмотреть его документацию .
На самом деле, я думаю, что в стандарте они описаны с точки зрения максимальной сложности различных операций (например, произвольный доступ в постоянное время, итерация по всем элементам в линейное время, добавление и удаление элементов в конце в постоянное амортизированное время, и т.д.), но у AFAIK нет другого метода выполнения таких требований, кроме использования динамического массива. Как утверждает @Lucretiel, стандарт фактически требует, чтобы элементы хранились непрерывно, поэтому это динамический массив, хранящийся там, где его размещает связанный распределитель.
Относительно вашей сноски: хотя и верно, стандарт также гарантирует, что арифметика указателей на внутренних элементах работает, а это значит, что это должен быть массив: & 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>:
У вас нет надежного доступа к базовому указателю, что может быть проблемой, если вы имеете дело со сторонними функциями, которые требуют адрес массива.
std::vector<bool>Класс глупо. Он реализован как сжатое битовое поле, а не как массив. Избегайте этого, если вы хотите массив bools!
Во время использования std::vector<T>s будет немного больше, чем массив C ++ с тем же количеством элементов. Это потому, что им нужно отслеживать небольшое количество другой информации, например их текущий размер, и потому что всякий раз, когда std::vector<T>размер s изменяется, они резервируют больше места, чем им нужно. Это предотвращает изменение размера при каждом добавлении нового элемента. Такое поведение можно изменить, предоставив кастом allocator, но я никогда не чувствовал необходимости делать это!
Изменить: После прочтения ответа Zud на вопрос, я чувствовал, что я должен добавить это:
std::array<T>Класс не такой же , как массив C ++. std::array<T>очень тонкая обертка вокруг массивов C ++, основная цель которой - скрыть указатель от пользователя класса (в C ++ массивы неявно приводятся как указатели, часто приводящие к ужасающему эффекту). std::array<T>Класс также хранит ее размер (длина), которая может быть очень полезным.
Это «так же быстро», как и использование динамически размещаемого встроенного массива. С другой стороны, использование автоматического массива может значительно отличаться по производительности (и не только во время распределения из-за эффектов локальности).
Бен Фойгт,
4
Для не булевых векторов в C ++ 11 и более поздних версиях вы можете вызвать data()a, std::vector<T>чтобы получить базовый указатель. Вы также можете просто взять адрес элемента 0 (гарантированно работать с C ++ 11, вероятно, будет работать с более ранними версиями).
Мэтт
16
Чтобы подчеркнуть точку зрения @MatteoItalia, разница в эффективности заключается в том, где хранятся данные. Куча памяти (требуется с vector) требует вызова системы для выделения памяти, и это может быть дорого, если вы считаете циклы. Стековая память (возможная для array) практически не требует затрат времени, потому что память выделяется путем простой настройки указателя стека, и это делается один раз при входе в функцию. Стек также позволяет избежать фрагментации памяти. Конечно, std::arrayне всегда будет в стеке; это зависит от того, где вы его выделите, но все равно будет выделяться на одну кучу меньше памяти из кучи по сравнению с вектором. Если у тебя есть
маленький «массив» (скажем, менее 100 элементов) - (типичный стек составляет около 8 МБ, поэтому не выделяйте больше, чем несколько КБ в стеке или меньше, если ваш код рекурсивный)
размер будет фиксированным
время жизни находится в области действия функции (или является значением члена с тем же временем жизни, что и у родительского класса)
ты считаешь циклы,
определенно используйте std::arrayнад вектором. Если какое-либо из этих требований не соответствует действительности, используйте a std::vector.
Хороший ответ. «Чтобы быть уверенным, 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 и заставить его работать.
std::vector
противstd::array
и как термины отличаются.std::array
это не то же самое, что массив C ++.std::array
очень тонкая оболочка для массивов C ++, основная цель которой - скрыть указатель от пользователя класса. Я обновлю свой ответ.Ответы:
std::vector
является шаблоном класса, который инкапсулирует динамический массив 1 , сохраненный в куче, который автоматически увеличивается и уменьшается при добавлении или удалении элементов. Он предоставляет все хуки (begin()
,end()
итераторы и т. Д.), Которые делают его работоспособным с остальной частью STL. У него также есть несколько полезных методов, которые позволяют вам выполнять операции, которые в обычном массиве будут громоздкими, например, вставлять элементы в середине вектора (он выполняет всю работу по перемещению следующих элементов за кулисами).Поскольку он хранит элементы в памяти, выделенной в куче, он имеет некоторые накладные расходы в отношении статических массивов.
std::array
это шаблонный класс, который инкапсулирует массив статического размера, хранящийся внутри самого объекта, что означает, что, если вы создадите экземпляр класса в стеке, сам массив будет в стеке. Его размер должен быть известен во время компиляции (он передается как параметр шаблона), и он не может увеличиваться или уменьшаться.Он более ограничен
std::vector
, но часто более эффективен, особенно для небольших размеров, потому что на практике это в основном облегченная оболочка вокруг массива в стиле C. Однако он более безопасен, поскольку неявное преобразование в указатель отключено и обеспечивает большую часть функций, связанных с STL,std::vector
и других контейнеров, поэтому его можно легко использовать с алгоритмами STL & co. Во всяком случае, для самого ограничения фиксированного размера это гораздо менее гибко, чемstd::vector
.Для ознакомления
std::array
посмотрите эту статью ; для быстрого ознакомления сstd::vector
операциями, которые возможны на нем, вы можете посмотреть его документацию .На самом деле, я думаю, что в стандарте они описаны с точки зрения максимальной сложности различных операций (например, произвольный доступ в постоянное время, итерация по всем элементам в линейное время, добавление и удаление элементов в конце в постоянное амортизированное время, и т.д.), но у AFAIK нет другого метода выполнения таких требований, кроме использования динамического массива.Как утверждает @Lucretiel, стандарт фактически требует, чтобы элементы хранились непрерывно, поэтому это динамический массив, хранящийся там, где его размещает связанный распределитель.источник
static
ключевое слово в C ++ имеет три разных значения, и этот термин также часто используется, чтобы говорить о вещах, которые исправлены во время компиляции. Я надеюсь, что «статический размер» немного более понятен.Используя
std::vector<T>
класс:... так же быстро, как и использование встроенных массивов, при условии, что вы делаете только то, что позволяют встроенные массивы (чтение и запись в существующие элементы).
... автоматически изменяет размер при вставке новых элементов.
... позволяет вставлять новые элементы в начале или в середине вектора, автоматически «сдвигая» остальные элементы «вверх» (имеет ли это смысл?). Это позволяет вам удалять элементы в любом месте
std::vector
, автоматически сдвигая остальные элементы вниз.... позволяет выполнять чтение с проверкой диапазона с помощью
at()
метода (вы всегда можете использовать индексаторы,[]
если не хотите, чтобы эта проверка выполнялась).Есть
дваосновных предостереженияstd::vector<T>
:У вас нет надежного доступа к базовому указателю, что может быть проблемой, если вы имеете дело со сторонними функциями, которые требуют адрес массива.
std::vector<bool>
Класс глупо. Он реализован как сжатое битовое поле, а не как массив. Избегайте этого, если вы хотите массивbool
s!Во время использования
std::vector<T>
s будет немного больше, чем массив C ++ с тем же количеством элементов. Это потому, что им нужно отслеживать небольшое количество другой информации, например их текущий размер, и потому что всякий раз, когдаstd::vector<T>
размер s изменяется, они резервируют больше места, чем им нужно. Это предотвращает изменение размера при каждом добавлении нового элемента. Такое поведение можно изменить, предоставив кастомallocator
, но я никогда не чувствовал необходимости делать это!Изменить: После прочтения ответа Zud на вопрос, я чувствовал, что я должен добавить это:
std::array<T>
Класс не такой же , как массив C ++.std::array<T>
очень тонкая обертка вокруг массивов C ++, основная цель которой - скрыть указатель от пользователя класса (в C ++ массивы неявно приводятся как указатели, часто приводящие к ужасающему эффекту).std::array<T>
Класс также хранит ее размер (длина), которая может быть очень полезным.источник
data()
a,std::vector<T>
чтобы получить базовый указатель. Вы также можете просто взять адрес элемента 0 (гарантированно работать с C ++ 11, вероятно, будет работать с более ранними версиями).Чтобы подчеркнуть точку зрения @MatteoItalia, разница в эффективности заключается в том, где хранятся данные. Куча памяти (требуется с
vector
) требует вызова системы для выделения памяти, и это может быть дорого, если вы считаете циклы. Стековая память (возможная дляarray
) практически не требует затрат времени, потому что память выделяется путем простой настройки указателя стека, и это делается один раз при входе в функцию. Стек также позволяет избежать фрагментации памяти. Конечно,std::array
не всегда будет в стеке; это зависит от того, где вы его выделите, но все равно будет выделяться на одну кучу меньше памяти из кучи по сравнению с вектором. Если у тебя естьопределенно используйте
std::array
над вектором. Если какое-либо из этих требований не соответствует действительности, используйте astd::vector
.источник
new std::array
или сделайте его членом класса, который вы используете 'new` для распределения.new std::array
все еще ожидает знать его размер во время компиляции и не может изменить его размер, но все еще живет в куче?new std::array
противnew std::vector
.Если вы рассматриваете возможность использования многомерных массивов, то есть одно дополнительное отличие между std :: array и std :: vector. У многомерного std :: array будут элементы, упакованные в память во всех измерениях, так же, как массив AC стиля. Многомерный std :: vector не будет упакован во всех измерениях.
Учитывая следующие декларации:
Указатель на первый элемент в массиве c-style (cConc) или std :: array (aConc) может быть повторен по всему массиву путем добавления 1 к каждому предыдущему элементу. Они плотно упакованы.
Указатель на первый элемент в векторном массиве (vConc) или массиве указателей (ptrConc) может быть повторен только через первые 5 (в данном случае) элементов, и затем для моей служебной информации существует 12 байтов (в моей системе) служебных данных. следующий вектор.
Это означает, что массив std :: vector>, инициализированный как массив [3] [1000], будет намного меньше в памяти, чем массив, инициализированный как массив [1000] [3], и оба будут больше в памяти, чем std: Массив выделяется в любом случае.
Это также означает, что вы не можете просто передать многомерный векторный массив (или указатель), скажем, в openGL без учета накладных расходов памяти, но вы можете наивно передать многомерный массив std :: array в openGL и заставить его работать.
источник
Вектор - это контейнерный класс, а массив - выделенная память.
источник
std::vector<T>
противT[]
, но вопрос оstd::vector<T>
противstd::array<T>
.