Следует ли использовать массивы в C ++?

96

Поскольку std::listи std::vectorсуществует, есть ли причина использовать традиционные массивы C в C ++ или их следует избегать, как и malloc?

Андреас
источник
18
@Als: этот вопрос касается разницы между двумя конкретными контейнерами, тогда как этот вопрос касается разницы между необработанными массивами и стандартными контейнерами в целом.
Джон Парди

Ответы:

109

В C ++ 11, где std::arrayэто возможно, ответ - «да, следует избегать массивов». До C ++ 11 вам может потребоваться использовать массивы C для размещения массивов в автоматическом хранилище (то есть в стеке).

dasblinkenlight
источник
3
Однако многие компиляторы все еще не поддерживают C ++ 11. Вам нужно будет решить, когда лучше использовать один, а не другой, учитывая отсутствие std :: array
Nowayz
4
std :: array - это шаблон, который влияет на большие проекты с точки зрения времени сборки и, возможно, размера кода, поскольку для каждой комбинации T, N шаблон создается заново.
zvrba
std :: vector гарантирует выравнивание данных по стандарту, поэтому его можно использовать практически везде. В C ++ 11 действительно нет причин использовать массивы C.
Nils
16
Массивы @Nils также гарантируют выравнивание. Кроме того, автоматическое выделение памяти («стек») происходит намного быстрее, чем динамическое выделение памяти. Если я знаю, что у меня ровно 3 элемента [например, координаты треугольника], нет причин использовать вектор.
zvrba
9
@zvrba - Проверяет сгенерированную сборку при использовании std :: array против массивов C. Вообще никакой разницы.
Nemanja Trifunovic
85

Определенно, хотя и std::arrayв C ++ 11, практически только для статических данных. Массивы в стиле C имеют три важных преимущества перед std::vector:

  • Им не требуется динамическое размещение. По этой причине массивы в стиле C предпочтительнее там, где у вас, вероятно, будет много очень маленьких массивов. Скажите что-нибудь вроде точки n размерности:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    Как правило, можно представить себе dimsочень маленький (2 или 3) Tвстроенный тип ( double), который в итоге может получить std::vector<Point>миллионы элементов. Вам определенно не нужны миллионы динамических распределений 3 double.

  • Поддержка статической инициализации. Это проблема только для статических данных, например:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    Это часто предпочтительнее использования вектора (и std::string), так как это позволяет избежать всех проблем с порядком инициализации; данные предварительно загружаются до того, как можно будет выполнить какой-либо фактический код.

  • Наконец, в связи с вышеизложенным, компилятор может вычислить фактический размер массива из инициализаторов. Их не нужно считать.

Если у вас есть доступ к C ++ 11, он std::arrayрешает первые две проблемы и, безусловно, должен использоваться вместо массивов стиля C в первом случае. Однако он не обращается к третьему, и наличие размерности массива в соответствии с количеством инициализаторов компилятором по-прежнему является веской причиной для предпочтения массивов в стиле C.

Джеймс Канце
источник
11
Инициализация массива в стиле C также избавляет от необходимости повторяться. int i[] = { 1, 2, 3 };продолжает работать с int i[] = { 1, 2, 3, 4 };. array<int, 3>необходимо вручную изменить на array<int, 4>.
10
@JoeWreschnig Изменение, о котором легко забыть. Если вы добавляете элемент, компилятор должен пожаловаться, но если вы его удалите, вы получите дополнительный инициализированный элемент 0 в конце. Я по-прежнему широко использую массивы в стиле C для такого рода статических данных.
Джеймс Канце
3
Первое предложение не имеет смысла.
Конрад Рудольф
4
Третий вопрос может быть решен довольно элегантно, используя make_arrayфункцию , аналогичную и make_pairт. Д. Подсказка к @R. Мартиньо Фернандес .
Конрад Рудольф
@KonradRudolph: Конечно. Андреас спрашивает: «Следует ли использовать массивы в C ++?», На что Джеймс отвечает: «Определенно, хотя std::arrayв C ++ 11 [их следует использовать] практически только для статических данных».
Джон Парди
15

Никогда не говорите «никогда», но я согласен с тем, что их роль значительно уменьшается из-за настоящих структур данных из STL.

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

Duffymo
источник
11

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

Эд Хил
источник
8
До C ++ 11 я бы согласился, но std::array<T>выделяет в стеках и в основном не имеет накладных расходов на необработанный массив.
111111
5
@ 111111 - Согласен. Но я знаю некоторых людей в этой отрасли, которые еще не перешли на C ++ 11
Эд Хил,
Я знаю, что именно поэтому я не голосовал против вас, но я думаю, что у boost была версия, и вы также легко можете свернуть свою собственную.
111111
6
но в критически важных для безопасности системах вы не используете новые функции компилятора (менее протестированные) и не используете ускорение.
Джеймс Канце
3
Многие критически важные для безопасности системы построены на СТАРЫХ компиляторах, которые даже не имеют новых функций компилятора, потому что смена инструментальных средств - медленный и дорогостоящий процесс, требующий тонны документов, тестирования и сертификации.
Брайан МакФарланд
6

arrayin c++дает вам фиксированный размер быстрой альтернативы динамического размера std::vectorи std::list. std :: array - одно из дополнений в c++11. Он обеспечивает преимущества контейнеров std, в то же время обеспечивая семантику агрегированного типа массивов в стиле C.

Поэтому c++11я бы обязательно использовал там std::array, где это требуется, over vector. Но я бы избежать массив стилей C в C++03.

Викас
источник
4

Чаще всего нет , я не могу придумать причины использовать необработанные массивы, скажем, вместо vectors. Если код новый .

Возможно, вам придется прибегнуть к использованию массивов, если ваши библиотеки должны быть совместимы с кодом, который ожидает массивы и необработанные указатели.

Лучиан Григоре
источник
1
... но начиная с C ++ 03 вектор "фактически имеет" массив, к которому вы можете получить доступ по указателю для чтения или записи. Таким образом, это охватывает большинство случаев кода, который ожидает указатели на массивы. На самом деле только тогда, когда этот код выделяет или освобождает массив, вы не можете использовать вектор.
Стив Джессоп,
@SteveJessop можно ли получить доступ к внутреннему массиву?
Лучиан Григоре
1
@LuchianGrigore: vector.data()в C ++ 11 или &vector.front()ранее.
Майк Сеймур,
@Luchian: при условии, что вектор не пуст, вы можете взять указатель на элемент (и если он пуст, вы можете передать нулевой указатель и длину 0 любой разумно написанной функции, которая принимает крайний случай буфер нулевого размера). Практически единственная цель гарантии смежности векторов, добавленной в C ++ 03, заключалась в том, чтобы разрешить использование векторов в качестве буферов с помощью кода, ориентированного на указатель.
Стив Джессоп,
1
@SteveJessop И тот факт, что многие люди думали, что это все равно гарантировано, и считалось предпочтительным не разочаровывать их.
Джеймс Канце
4

Я знаю, что многие люди указывают на std :: array для выделения массивов в стеке и std :: vector для кучи. Но ни один из них, похоже, не поддерживает неродное выравнивание. Если вы используете какой-либо числовой код, в котором хотите использовать инструкции SSE или VPX (что требует 128 или 256 байтов выравнивания соответственно), массивы C по-прежнему будут вашим лучшим выбором.

gct
источник
3

Я бы сказал, что массивы по-прежнему полезны, если вы храните небольшой статический объем данных, почему бы и нет.

Джеймс82345
источник
2

Единственное преимущество массива (конечно, обернутого во что-то, что будет автоматически управлять его освобождением при необходимости) над, о котором std::vectorя могу думать, заключается в том, что он vectorне может передать право собственности на свои данные, если ваш компилятор не поддерживает C ++ 11 и конструкторы перемещения.

Тадеуш Копец
источник
6
«вектор не может передать право собственности на свои данные» - да, в C ++ 03 может, используя swap.
Стив Джессоп,
2

Массивы в стиле C - это фундаментальная структура данных, поэтому будут случаи, когда ее лучше использовать. Однако в общем случае используйте более сложные структуры данных, которые скругляют углы базовых данных. C ++ позволяет делать с памятью очень интересные и полезные вещи, многие из которых работают с простыми массивами.

Джеймс Винн
источник
3
Почему массивы в стиле C более фундаментальны, чем std::arrays? Оба во многих случаях будут скомпилированы в одну и ту же сборку.
уехал примерно
1
Более фундаментальный в том смысле, что он более простой. Вы знаете, что собирается делать массив, std :: array может иметь особенности реализации, поскольку он полагается на стандартную библиотеку.
Джеймс Винн
1
@JamesWynn Не совсем. std::arrayимеет точно определенную семантику, построенную на основе статических массивов.
Конрад Рудольф
1

Вы должны использовать контейнеры STL внутри, но вы не должны передавать указатели на такие контейнеры между различными модулями, иначе вы попадете в ад зависимостей. Пример:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

это очень хорошее решение, но не

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

Причина в том, что std :: string можно реализовать разными способами, но строка в стиле c всегда является строкой в ​​стиле c.

user877329
источник
Я думаю, вы пытаетесь сказать: не связывайте вместе разные объектные коды, если для их создания использовались разные компиляторы / реализации стандартной библиотеки. Это, конечно, правда. Как это связано с исходным вопросом?
jogojapan
Это просто совет, когда использовать массивы или контейнеры STL. Создавайте данные с помощью контейнера, передавайте их как массив. Для других данных в строках у вас будет что-то вроде myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329
Но эти проблемы возникают только тогда, когда вы объединяете разные реализации стандартной библиотеки в одном проекте. Это безумие. В любом нормальном фрагменте кода вполне нормально передать, скажем, вектор по ссылке (или, в C ++ 11, переместить его) в функцию.
jogojapan
1
Мне просто нравятся плагины
user877329