На предыдущих уроках мы подробно говорили о фиксированных и динамических массивах. Хотя они очень полезны и активно используются в языке 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++ использует термин «размер» для обозначения длины массива — не путайте это с результатами выполнения оператора 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 вместо стандартных фиксированных массивов в любых нетривиальных задачах.
Мгер, Кто вам такое сказал ?
Ваш код с обычным двумерным массивом легко справляется.
Если вы не знаете как это сделать, то это не значит что невозможно.
Не вводите людей в заблуждение
Кстати, начиная с C++17 компилятор может вывести не только длину, но и тип массива:
Можете объяснить этот момент?
Почему елементам 2 и 3, а не 1 и 2 ?
тут все что не заполнено, получает значение по умолчанию.
так же как и с обычным массивом:
"std::array — это отличная замена стандартных фиксированных массивов. Они более эффективны, так как используют меньше памяти." Это вдруг почему?
Не по чему. На деле, std::array и обычный массив подуцируют один и тот же ассемблерный код (проверял в VS). С std::array удобнее присваивание целого массива, передача в функции, копирование и т. д. (с обычными массивами тут большие проблемы). По эффективности они абсолютно идентичны
Ну конечно array будет знать свой размер , если указывать его в параметрах :
А если функция должна работать с разными array. Передавать размер как аргумент или я что то не понял?
Не совсем то что ты хочешь, но вот еще как можно сделать:
А как на счет двумерного массива?
Двумерный массив объявляется как:
Здесь myarray — это название вашего двумерного массива, а сам двумерный массив создается с помощью вложенного массива (т.е. один внутри другого).
Это очень неудобно. Тут лучше использовать обычные квадратные скобки
с обычным массивом много что нельзя сделать. Например это:
Покажите синтаксис использования функции at() для двухмерного массива и почему size() не показывает суммарную длину всех ячеек двухмерного массива?
Спасибо alex_1988 за вопросы.
И конечно автору сайта за качественный перевод.
Синтаксис использования функции at() в двумерных массивах выглядит так: myarray2.at().at()
Но есть особенность, например:
Здесь сначала идет проверка последнего (второго) индекса массива myarray2 (объявлено 5, проверяем от 0 до 4), а затем предпоследнего (первого) индекса массива myarray2 (объявлено 10, проверяем от 0 до 9).