std :: vector versus std :: array в C ++

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

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

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

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

    Он более ограничен, чем std::vector , но он часто более эффективен, особенно для небольших размеров, потому что на практике это в основном облегченная shell вокруг массива C-стиля. Однако он более безопасен, поскольку неявное преобразование в указатель отключено, и оно обеспечивает большую часть связанных с STL функциональных возможностей std::vector и других контейнеров, поэтому вы можете легко использовать его с алгоритмами STL & co. Во всяком случае, для самого ограничения фиксированного размера он гораздо менее гибкий, чем std::vector .

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


    1. На самом деле, я считаю, что в стандарте они описываются с точки зрения максимальной сложности различных операций (например, случайный доступ в постоянное время, итерация по всем элементам в линейном времени, добавление и удаление элементов в конце в постоянное время амортизации, и т. д.), но AFAIK нет другого метода выполнения таких требований, кроме использования динамического массива. Как указано в @Lucretiel, стандарт фактически требует, чтобы элементы хранились смежно, поэтому это динамический массив, который хранится там, где его назначает соответствующий распределитель.

    Использование std::vector :

    • так же быстро, как и встроенные массивы, предполагая, что вы делаете только то, что позволяют встроенные массивы (чтение и запись в существующие элементы).

    • … автоматически изменяет размер при вставке новых элементов.

    • … позволяет вставлять новые элементы в начале или в середине вектора, автоматически «перемещая» остальные элементы «вверх» (это имеет смысл?). Он позволяет удалять элементы в любом месте std::vector , также автоматически перемещая остальные элементы вниз.

    • … позволяет выполнять проверенное диапазоном чтение с помощью метода at() (вы всегда можете использовать индексы [] если вы не хотите, чтобы эта проверка выполнялась).

    Существует два основных способа использования std::vector :

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

    2. Класс std::vector является глупым. Он реализован как сжатое битовое поле, а не как массив. Избегайте его, если вы хотите массив bool s!

    3. Во время использования std::vector s будет немного больше, чем массив C ++ с таким же количеством элементов. Это связано с тем, что им необходимо отслеживать небольшое количество другой информации, такой как их текущий размер, и потому, что при изменении размера std::vector s они оставляют больше места, чем нужно. Это делается для того, чтобы не изменять их каждый раз при вставке нового элемента. Такое поведение можно изменить, предоставив пользовательский allocator , но я никогда не чувствовал необходимости делать это!


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

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

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

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

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

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

    Учитывая следующие заявления:

     int cConc[3][5]; std::array, 3> aConc; int **ptrConc; // initialized to [3][5] via new and destructed via delete std::vector> 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 и заставить его работать.

    Одно из преимуществ, которое векторы имеют над массивами, состоит в том, что можно найти текущий размер вектора, используя vector_name.size () .

    Как вы можете себе представить, это может быть весьма полезно в самых разных ситуациях, где вы можете легко получить количество элементов в массиве.

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

    Давайте будем гением компьютера.