На попередніх уроках ми розглядали фіксовані і динамічні масиви. Хоча вони дуже корисні і активно використовуються в мові C++, у них також є свої недоліки: фіксовані масиви конвертуються у вказівники, втрачаючи інформацію про свою довжину; в динамічних масивах проблеми можуть виникнути зі звільненням пам’яті і зі спробами змінити довжину масивів після виділення.
Тому в Стандартну бібліотеку C++ додали функціонал, який спрощує процес управління масивами: std::array і std::vector. На цьому уроці ми розглянемо std::array, а на наступному — std::vector.
Введення в std::array
Представлений в C++11, std::array — це фіксований масив, який не конвертується у вказівник при передачі в функцію. std::array визначається в заголовковому файлі array, всередині простору імен std. Оголошення змінної std::array наступне:
1 2 3 |
#include <array> std::array<int, 4> myarray; // оголошуємо масив типу int довжиною 4 |
Подібно звичайним фіксованим масивам, довжина std::array повинна бути встановлена під час компіляції. std::array можна ініціалізувати за допомогою списку ініціалізаторів або uniform-ініціалізації:
1 2 |
std::array<int, 4> myarray = { 8, 6, 4, 1 }; // список ініціалізаторів std::array<int, 4> myarray2 { 8, 6, 4, 1 }; // uniform-ініціалізація |
На відміну від стандартних фіксованих масивів, в std::array ви не можете пропустити (не вказувати) довжину масиву:
1 |
std::array<int, > myarray = { 8, 6, 4, 1 }; // заборонено, повинна бути вказана довжина масиву |
Також можна присвоювати значення масиву за допомогою списку ініціалізаторів:
1 2 3 4 |
std::array<int, 4> myarray; myarray = { 0, 1, 2, 3 }; // ок myarray = { 8, 6 }; // ок, елементам 2 і 3 присвоєно нуль! myarray = { 0, 1, 3, 5, 7, 9 }; // заборонено, занадто багато елементів в списку ініціалізаторів! |
Доступ до значень масиву через оператор індексу здійснюється як зазвичай:
1 2 |
std::cout << myarray[1]; myarray[2] = 7; |
Так само, як і в стандартних фіксованих масивах, оператор індексу не виконує ніяких перевірок на діапазон. Якщо вказано некоректний індекс, то відбудуться погані речі.
std::array підтримує ще одну форму доступу до елементів масиву — функція at(), яка здійснює перевірку діапазону:
1 2 3 |
std::array<int, 4> myarray { 8, 6, 4, 1 }; myarray.at(1) = 7; // елемент масиву під номером 1 - коректний, присвоюємо йому значення 7 myarray.at(8) = 15; // елемент масиву під номером 8 - некоректний, отримуємо помилку |
У прикладі, наведеному вище, виклик myarray.at(1)
перевіряє, чи є елемент масиву під номером 1, і, оскільки він є, повертається посилання на цей елемент. Потім ми присвоюємо йому значення 7
. Проте, виклик myarray.at(8)
не спрацьовує, так як елементу під номером 8 у масиві немає. Замість повернення посилання, функція at() видає помилку, яка завершує роботу програми (насправді генерується виняток типу std::out_of_range. Про винятки ми поговоримо трішки пізніше). Оскільки перевірка діапазону виконується, то функція at() працює повільніше (але безпечніше), ніж оператор []
.
std::array автоматично робить очистку після себе, коли виходить з області видимості, тому немає необхідності прописувати це вручну.
Розмір і сортування
За допомогою функції size() можна дізнатися довжину масиву:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> #include <array> int main() { std::array<double, 4> myarray{ 8.0, 6.4, 4.3, 1.9 }; std::cout << "length: " << myarray.size(); return 0; } |
Результат:
length: 4
Оскільки std::array не конвертується у вказівник при передачі у функцію, то функція size() працюватиме, навіть якщо її викликати з іншої функції:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> #include <array> void printLength(const std::array<double, 4> &myarray) { std::cout << "length: " << myarray.size(); } int main() { std::array<double, 4> myarray { 8.0, 6.4, 4.3, 1.9 }; printLength(myarray); return 0; } |
Результат такий же:
length: 4
Зверніть увагу, Cтандартна бібліотека C++ використовує термін «розмір» для позначення довжини масиву — не плутайте це з результатами виконання оператора sizeof зі звичайним фіксованим масивом, коли повертається фактичний розмір масиву в пам’яті (розмір_елементу * довжина_масиву
).
Також зверніть увагу на те, що ми передаємо std::array по (константному) посиланню. Це робиться для того, щоб компілятор не виконував копіювання масиву при передачі в функцію.
Правило: Завжди передавайте std::array в функцію по звичайному або константному посиланню.
Оскільки довжина масиву завжди відома, то цикли foreach також можна використовувати з std::array:
1 2 3 4 |
std::array<int, 4> myarray { 8, 6, 4, 1 }; for (auto &element : myarray) std::cout << element << ' '; |
Ви можете відсортувати std::array, використовуючи функцію std::sort(), яка знаходиться в заголовку algorithm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> #include <array> #include <algorithm> // для std::sort int main() { std::array<int, 5> myarray { 8, 4, 2, 7, 1 }; std::sort(myarray.begin(), myarray.end()); // сортування масиву по зростанню // std::sort(myarray.rbegin(), myarray.rend()); // сортування масиву по спаданню for (const auto &element : myarray) std::cout << element << ' '; return 0; } |
Результат:
1 2 4 7 8
Функція сортування використовує ітератори, які ми ще не розглядали. Про них ми поговоримо на відповідному уроці.
Висновки
std::array — це чудова заміна стандартним фіксованим масивам. Масиви, створені за допомогою std::array, більш ефективні, так як використовують менше пам’яті. Єдиними недоліками std::array в порівнянні зі стандартними фіксованими масивами є трохи незручний синтаксис і те, що потрібно явно вказувати довжину масиву (компілятор не обчислюватиме її за нас). Але це порівняно незначні нюанси. Рекомендується використовувати std::array замість стандартних фіксованих масивів в будь-яких нетривіальних завданнях.