Чи слід використовувати масиви в C ++?


95

Оскільки існує std::listі std::vectorіснує, чи є причина використовувати традиційні масиви C в C ++, або їх слід уникати, як malloc?



18
@Als: Це питання стосується різниці між двома конкретними контейнерами, тоді як це питання стосується різниці між необробленими масивами та стандартними контейнерами загалом.
Джон Перді,

Відповіді:


109

У C ++ 11, де std::arrayце можливо, відповідь: "так, слід уникати масивів". До C ++ 11 вам може знадобитися використовувати масиви C, щоб розподілити масиви в автоматичному сховищі (тобто в стеку).


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 масивів. Ніякої різниці взагалі.
Неманья Трифунович

85

Безумовно, хоча і std::arrayв C ++ 11, практично лише для статичних даних. Масиви в стилі С мають три важливі переваги перед std::vector:

  • Вони не потребують динамічного розподілу. З цієї причини слід віддавати перевагу масивам стилю C там, де у вас, ймовірно, буде багато дуже маленьких масивів. Скажіть щось на зразок точки n-виміру:

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

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

  • Підтримка статичної ініціалізації. Це проблема лише для статичних даних, де щось на зразок:

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

    Це часто переважніше використання вектора (і std::string), оскільки це дозволяє уникнути усіх питань порядку ініціалізації; дані попередньо завантажуються, перш ніж будь-який фактичний код може бути виконаний.

  • Нарешті, що стосується вищесказаного, компілятор може обчислити фактичний розмір масиву за ініціалізаторами. Не потрібно їх рахувати.

Якщо у вас є доступ до C ++ 11, std::arrayвирішує перші дві проблеми, і, безумовно, слід використовувати перевагу масивів стилю C у першому випадку. Однак це не стосується третього, і наявність розміру компілятора масиву відповідно до кількості ініціалізаторів все ще є вагомою причиною віддавати перевагу масивам стилю C.


11
Ініціалізація масиву в стилі С також позбавляє від необхідності повторюватись. 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.

Я б також сказав, що інкапсуляція всередині об’єктів повинна звести до мінімуму вплив такого вибору. Якщо масив є приватним членом даних, ви можете поміняти його місцями або змінити, не впливаючи на клієнтів вашого класу.


11

Я працював над критичними системами безпеки, де ви не можете використовувати динамічне розподіл пам'яті. Пам'ять завжди повинна бути в стеку. Тому в цьому випадку ви використовуєте масиви, оскільки розмір фіксується під час компіляції.


8
До C ++ 11 я б погодився, але std::array<T>розподіляє по стеках і в основному не має накладних витрат на необроблений масив.
111111,

5
@ 111111 - Погоджено. Але я знаю, що деякі люди в цій галузі ще не перейшли на C ++ 11
Ед Хіл

Я знаю, що тому я не проголосував проти вас, але я думаю, що у boost була версія, і її також легко прокрутити.
111111,

6
але в системах, що мають важливе значення для безпеки, ви не використовуєте нові функції компілятора (менш перевірені) і не використовуєте boost.
Джеймс Канце,

3
Багато систем, що мають важливе значення для безпеки, побудовані на старих компіляторах, які навіть не мають нових функцій компілятора, оскільки зміна ланцюжків інструментів - це повільний, дорогий процес, що вимагає безлічі паперів, тестування та сертифікації.
Брайан Макфарланд

6

arrayin c++дає вам швидкий варіант фіксованого розміру динамічного розміру std::vectorта std::list. std :: array - одне з доповнень у c++11. Це забезпечує переваги контейнерів std, одночасно забезпечуючи сукупну семантику типів масивів у стилі C.

Тому c++11я б, звичайно, використовував std::array, де це потрібно, над вектором. Але я б уникнути масив стилів C в C++03.


4

Більшість зазвичай, немає , я не можу думати про причини використання сировинних масивів над, скажімо, vectors. Якщо код новий .

Можливо, вам доведеться вдатися до використання масивів, якщо ваші бібліотеки повинні бути сумісними з кодом, який очікує на масиви та необроблені вказівники.


1
... але оскільки C ++ 03 вектор "насправді має" масив, до якого ви можете отримати доступ за допомогою вказівника для читання або запису. Отже, це охоплює більшість випадків коду, який очікує покажчиків на масиви. Лише насправді, коли цей код виділяє або звільняє масив, ви не можете використовувати вектор.
Стів Джессоп,

@SteveJessop чи можете ви отримати доступ до внутрішнього масиву?
Luchian Grigore

1
@LuchianGrigore: vector.data()у C ++ 11 або &vector.front()раніше.
Майк Сеймур

@Luchian: за умови, що вектор не порожній, ви можете взяти покажчик на елемент (а якщо він порожній, ви можете передати нульовий вказівник і довжину 0 будь-якій розумно записаній функції, яка приймає регістр краю буфер нульового розміру). Практично єдиною метою векторної гарантії суміжності, доданої в C ++ 03, було дозволити векторам використовувати як буфери за допомогою орієнтованого на вказівник коду.
Стів Джессоп,

1
@SteveJessop І той факт, що багато людей вважали, що це так чи інакше гарантовано, і вважалося кращим не розчаровувати їх.
Джеймс Канце,

4

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


3

Я б сказав, що масиви все ще корисні, якщо ви зберігаєте невеликий статичний обсяг даних, чому б і ні.


2

Єдина перевага масиву (звичайно, загорнутого в щось, що автоматично управлятиме його вивільненням у разі потреби) над тим, про що std::vectorя можу подумати, це те, що він vectorне може передавати право власності на свої дані, якщо ваш компілятор не підтримує C ++ 11 та не переміщує конструктори.


6
"вектор не може передати право власності на свої дані" - так, він може, в C ++ 03, використовуючи swap.
Стів Джессоп,

2

Масиви стилю С є фундаментальною структурою даних, тому траплятимуться випадки, коли краще використовувати її. Однак для загального випадку використовуйте вдосконаленіші структури даних, які заокруглюють кути базових даних. C ++ дозволяє робити дуже цікаві та корисні речі з пам'яттю, багато з яких працюють із простими масивами.


3
Як масиви у стилі С є більш фундаментальними, ніж 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.


Я думаю, що ви намагаєтесь сказати: не зв'язуйте різні об'єктні коди разом, якщо для їх створення використовувались різні компілятори / реалізації стандартної бібліотеки. Це, звичайно, правда. Як це стосується вихідного питання?
jogojapan

Це просто порада, коли використовувати масиви або контейнери STL. Побудуйте дані за допомогою контейнера, передайте їх як масив. Для інших даних із рядками у вас буде щось на зразок myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329

Але ці проблеми виникають лише тоді, коли ви поєднуєте різні реалізації стандартної бібліотеки в одному проекті. Це божевільно. У будь-якому звичайному фрагменті коду цілком нормально передавати, скажімо, вектор за допомогою посилання (або, в C ++ 11, перемістити його) до функції.
jogojapan

1
Мені просто подобаються плагіни
user877329
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.