Урок №94. Введение в std::array

  Юрий  | 

  Обновл. 15 Янв 2021  | 

 50787

 ǀ   18 

На предыдущих уроках мы подробно говорили о фиксированных и динамических массивах. Хотя они очень полезны и активно используются в языке C++, у них также есть свои недостатки: фиксированные массивы распадаются в указатели, теряя информацию о своей длине; в динамических массивах проблемы могут возникнуть с освобождением памяти и с попытками изменить их длину после выделения.

Поэтому в Стандартную библиотеку C++ добавили функционал, который упрощает процесс управления массивами: std::array и std::vector. На этом уроке мы рассмотрим std::array, а на следующем — std::vector.

Введение в std::array

Представленный в C++11, std::array — это фиксированный массив, который не распадается в указатель при передаче в функцию. std::array определяется в заголовочном файле array, внутри пространства имен std. Объявление переменной std::array следующее:

Подобно обычным фиксированным массивам, длина std::array должна быть установлена во время компиляции. std::array можно инициализировать с использованием списка инициализаторов или uniform-инициализации:

В отличие от стандартных фиксированных массивов, в std::array вы не можете пропустить (не указывать) длину массива:

Также можно присваивать значения массиву с помощью списка инициализаторов:

Доступ к значениям массива через оператор индекса осуществляется как обычно:

Так же, как и в стандартных фиксированных массивах, оператор индекса не выполняет никаких проверок на диапазон. Если указан недопустимый индекс, то произойдут плохие вещи.

std::array поддерживает вторую форму доступа к элементам массива — функция at(), которая осуществляет проверку диапазона:

В примере, приведенном выше, вызов myarray.at(1) проверяет, есть ли элемент массива под номером 1, и, поскольку он есть, возвращается ссылка на этот элемент. Затем мы присваиваем ему значение 7. Однако, вызов myarray.at(8) не срабатывает, так как элемента под номером 8 в массиве нет. Вместо возвращения ссылки, функция at() выдает ошибку, которая завершает работу программы (на самом деле выбрасывается исключение типа std::out_of_range. Об исключениях мы поговорим на соответствующих уроках). Поскольку проверка диапазона выполняется, то функция at() работает медленнее (но безопаснее), чем оператор [].

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

Размер и сортировка


С помощью функции size() можно узнать длину массива:

Результат:

length: 4

Поскольку std::array не распадается в указатель при передаче в функцию, то функция size() будет работать, даже если её вызвать из другой функции:

Результат тот же:

length: 4

Обратите внимание, Стандартная библиотека C++ использует термин «размер» для обозначения длины массива — не путайте это с результатами выполнения оператора sizeof с обычным фиксированным массивом, когда возвращается фактический размер массива в памяти (размер элемента * длина массива).

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

Правило: Всегда передавайте std::array в функции по обычной или по константной ссылке.

Поскольку длина массива всегда известна, то циклы foreach также можно использовать с std::array:

Вы можете отсортировать std::array, используя функцию std::sort(), которая находится в заголовочном файле algorithm:

Результат:

1 2 4 7 8

Функция сортировки использует итераторы, которые мы еще не рассматривали. О них мы поговорим несколько позже.

Заключение

std::array — это отличная замена стандартных фиксированных массивов. Массивы, созданные с помощью std::array, более эффективны, так как используют меньше памяти. Единственными недостатками std::array по сравнению со стандартными фиксированными массивами являются немного неудобный синтаксис и то, что нужно явно указывать длину массива (компилятор не будет вычислять её за нас). Но это сравнительно незначительные нюансы. Рекомендуется использовать std::array вместо стандартных фиксированных массивов в любых нетривиальных задачах.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (244 оценок, среднее: 4,93 из 5)
Загрузка...

Комментариев: 18

  1. Аватар Михаил:

    Здравствуйте!

    Извините за частный случай: пишу программу для Wemos D1 mini Pro (головное устройство), для удобства работы с EEPROM, в работающую структуру, объявленную глобально, кроме прочего, был добавлен еще и двумерный массив в виде std:array для хранения адресов и статусов ведомых устройств:

    Все компилируется без предупреждений, но на строке ввода значения в массив, программа вылетает с "exception 29" — несанкционированная попытка записи в запрещенную для записи область памяти.
    ВОПРОС: как правильно вводить данные в двумерный массив помещенный в структуру из других участков программы?

    С уважением.

    1. Дмитрий Бушуев Дмитрий Бушуев:

      У меня всё скомпилировалось (как онлайн, так и на VS 2019 16.8.6) и выдало ожидаемый результат:
      https://onlinegdb.com/B1vrOJSfO

      1. Аватар Михаил:

        Дмитрий, у меня тоже все компилируется, проблема возникает после заливки скомпилированной прошивки в устройство. Но, на 19-й строчке моего примера у Wemos начинается когнитивный диссонанс ввиде переполнение стека и вываливается исключение 29 (попытка записи в область памяти недоступную для записи):

        21:55:14.004 ->
        21:55:14.004 -> Exception (29):
        21:55:14.004 -> epc1=0x40219fe1 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
        21:55:14.004 ->
        21:55:14.004 -> >>>stack>>>
        21:55:14.004 ->
        21:55:14.004 -> ctx: cont
        21:55:14.004 -> sp: 3ffff700 end: 3fffffc0 offset: 01a0
        21:55:14.004 -> 3ffff8a0: 3ffe0030 00000450 3ffffd24 4020fd02
        21:55:14.004 -> 3ffff8b0: 3ffef990 00000468 3ffefe84 3ffff8e0
        21:55:14.098 -> 3ffff8c0: 3ffef6b4 00000438 3ffef6b4 4020fe0e
        и т.п. и т.д.
        Причем при записи значения в аналогичный массив внутри этой же программы, но не вложенный в структуру, переполнения не происходит. Поэтому я и посчитал, что возможно я неправильно обращаюсь к такому массиву…

  2. Аватар Vlad:

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

  3. Аватар Георгий:

    Кстати, начиная с C++17 компилятор может вывести не только длину, но и тип массива:

  4. Аватар Снова я:

    Можете объяснить этот момент?

    Почему елементам 2 и 3, а не 1 и 2 ?

    1. Аватар Мгер:

      тут все что не заполнено, получает значение по умолчанию.
      так же как и с обычным массивом:

  5. Аватар Антон:

    "std::array — это отличная замена стандартных фиксированных массивов. Они более эффективны, так как используют меньше памяти." Это вдруг почему?

    1. Аватар Георгий:

      Не по чему. На деле, std::array и обычный массив подуцируют один и тот же ассемблерный код (проверял в VS). С std::array удобнее присваивание целого массива, передача в функции, копирование и т. д. (с обычными массивами тут большие проблемы). По эффективности они абсолютно идентичны

  6. Аватар Арман:

    Ну конечно array будет знать свой размер , если указывать его в параметрах :

    А если функция должна работать с разными array. Передавать размер как аргумент или я что то не понял?

    1. Аватар Мгер:

      Не совсем то что ты хочешь, но вот еще как можно сделать:

  7. Аватар Андрей:

    А как на счет двумерного массива?

    1. Юрий Юрий:

      Двумерный массив объявляется как:

      Здесь myarray — это название вашего двумерного массива, а сам двумерный массив создается с помощью вложенного массива (т.е. один внутри другого).

      1. Аватар Vlad:

        Это очень неудобно. Тут лучше использовать обычные квадратные скобки

        1. Аватар Мгер:

          с обычным массивом много что нельзя сделать. Например это:

      2. Аватар alex_1988:

        Покажите синтаксис использования функции at() для двухмерного массива и почему size() не показывает суммарную длину всех ячеек двухмерного массива?

        1. Аватар Сергей:

          Спасибо alex_1988 за вопросы.
          И конечно автору сайта за качественный перевод.

          Синтаксис использования функции at() в двумерных массивах выглядит так: myarray2.at().at()

          Но есть особенность, например:

          Здесь сначала идет проверка последнего (второго) индекса массива myarray2 (объявлено 5, проверяем от 0 до 4), а затем предпоследнего (первого) индекса массива myarray2 (объявлено 10, проверяем от 0 до 9).

      3. Аватар Евгений:

        А как многомерный std::array инициализировать?

        Пишет, слишком много инициализаторов.
        А кажется, что в самый раз

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *