Массивы в языке Си
При решении задач с большим количеством данных одинакового типа использование переменных с различными именами, не упорядоченных по адресам памяти, затрудняет программирование. В подобных случаях в языке Си используют объекты, называемые массивами.
Массив — это непрерывный участок памяти, содержащий последовательность объектов одинакового типа, обозначаемый одним именем.
Массив характеризуется следующими основными понятиями:
Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:
Адрес массива – адрес начального элемента массива.
Имя массива – идентификатор, используемый для обращения к элементам массива.
Размер массива – количество элементов массива
Размер элемента – количество байт, занимаемых одним элементом массива.
Графически расположение массива в памяти компьютера можно представить в виде непрерывной ленты адресов.
Длина массива – количество байт, отводимое в памяти для хранения всех элементов массива.
ДлинаМассива = РазмерЭлемента * КоличествоЭлементов
Для определения размера элемента массива может использоваться функция
Массивы (C++)
Массив — это последовательность объектов того же типа, которые занимают смежную область памяти. Традиционные массивы в стиле C являются источником многих ошибок, но по-прежнему являются общими, особенно в старых базах кода. В современных C++ мы настоятельно рекомендуем использовать std:: Vector или std:: Array вместо массивов в стиле C, описанных в этом разделе. Оба этих типа стандартных библиотек хранят свои элементы в виде непрерывного блока памяти. Однако они обеспечивают гораздо большую безопасность типов и итераторы поддержки, которые гарантированно указывают на допустимое расположение в последовательности. Дополнительные сведения см. в разделе контейнеры.
Объявления стека
В объявлении массива C++ размер массива указывается после имени переменной, а не после имени типа, как в некоторых других языках. В следующем примере объявляется массив значений типа Double 1000, которые будут выделяться в стеке. Число элементов должно быть указано как целочисленный литерал или else в качестве константного выражения. Это обусловлено тем, что компилятору необходимо выяснить, сколько пространства стека следует выделить; оно не может использовать значение, вычисленное во время выполнения. Каждому элементу массива присваивается значение по умолчанию 0. Если не назначить значение по умолчанию, каждый элемент изначально будет содержать случайные значения, находящимся в этой области памяти.
Массив нулевого размера допустим только в том случае, если массив является последним полем в struct или union и если расширения Microsoft включены ( /Za или /permissive- не заданы).
Массивы на основе стека быстрее выделяются и получают доступ, чем массивы на основе кучи. Однако пространство стека ограничено. Число элементов массива не может быть настолько большим, что в нем используется слишком много памяти стека. Насколько сильно зависит от программы. Для определения того, является ли массив слишком большим, можно использовать средства профилирования.
Объявления кучи
Может потребоваться, чтобы массив был слишком большим для выделения в стеке или его размер не известен во время компиляции. Можно выделить этот массив в куче с помощью new[] выражения. Оператор возвращает указатель на первый элемент. Оператор индекса работает с переменной-указателем так же, как и с массивом на основе стека. Также можно использовать арифметические операции с указателями для перемещения указателя на произвольные элементы в массиве. Вы обязаны убедиться в том, что:
В следующем примере показано, как определить массив в куче во время выполнения. В нем показано, как получить доступ к элементам массива с помощью оператора индекса и с помощью арифметики указателей:
Инициализация массивов
Можно инициализировать массив в цикле, по одному элементу за раз или в одной инструкции. Содержимое следующих двух массивов идентично:
Передача массивов в функции
Когда массив передается в функцию, он передается в качестве указателя на первый элемент, независимо от того, является ли он массивом на основе стека или кучи. Указатель не содержит дополнительных сведений о размере или типе. Такое поведение называется указателем Decay. При передаче массива в функцию необходимо всегда указывать количество элементов в отдельном параметре. Такое поведение также подразумевает, что элементы массива не копируются, когда массив передается в функцию. Чтобы запретить функции изменять элементы, укажите параметр в качестве указателя на const элементы.
В следующем примере показана функция, которая принимает массив и длину. Указатель указывает на исходный массив, а не на копию. Поскольку параметр не const имеет значение, функция может изменять элементы массива.
Объявите и определите параметр массива p так, const чтобы он был доступен только для чтения в блоке функции:
Одна и та же функция может также быть объявлена в таких случаях без изменения поведения. Массив по-прежнему передается в качестве указателя на первый элемент:
Многомерные массивы
Массивы, созданные из других массивов, являются многомерными. Такие многомерные массивы определяются путем последовательного размещения нескольких константных выражений, заключенных в квадратные скобки. Рассмотрим, например, следующее объявление:
Он задает массив типа, по int сути упорядоченный в двумерной матрице из пяти строк и семи столбцов, как показано на следующем рисунке.
Концептуальная структура многомерного массива
Можно объявить многомерные массивы, имеющие список инициализаторов (как описано в разделе инициализаторы). В этих объявлениях константное выражение, указывающее границы для первого измерения, может быть опущено. Пример:
В показанном выше объявлении определяется массив, состоящий из трех строк и четырех столбцов. Строки представляют фабрики, а столбцы — рынки, на которые фабрики поставляют свою продукцию. Значения — это стоимости транспортировки с фабрик на рынки. Первое измерение массива опущено, но компилятор заполняет его, проверяя инициализатор.
Использование оператора косвенного обращения (*) в n-мерном массиве приводит к получению n-1 многомерного массива. Если n равно 1, создается скаляр (или элемент массива).
Массивы C++ размещаются в памяти по срокам. Построчный порядок означает, что быстрее всего изменяется последний индекс.
Пример
Можно также опустить спецификацию границ для первого измерения многомерного массива в объявлениях функций, как показано ниже:
Эта функция FindMinToMkt написана таким, что добавление новых фабрик не требует каких-либо изменений кода, а только перекомпиляции.
Инициализация массивов
Массивы объектов, имеющих конструктор класса, инициализируются конструктором. Если в списке инициализаторов меньше элементов, чем элементов массива, то для остальных элементов используется конструктор по умолчанию. Если для класса не определен конструктор по умолчанию, список инициализаторов должен быть завершен, то есть должен быть один инициализатор для каждого элемента в массиве.
Статические массивы членов ( const вне зависимости от объявления класса) могут быть инициализированы в своих определениях. Пример:
Доступ к элементам массива
К отдельным элементам массива можно обращаться при помощи оператора индекса массива ( [ ] ). При использовании имени одномерного массива без индекса он вычисляется как указатель на первый элемент массива.
Если используются многомерные массивы, в выражениях можно использовать различные сочетания.
Перегрузка оператора индекса
Как и другие операторы, оператор индекса ( [] ) может быть переопределен пользователем. Поведение оператора индекса по умолчанию, если он не перегружен, — совмещать имя массива и индекс с помощью следующего метода.
Как и во всех дополнениех, включающих типы указателей, масштабирование выполняется автоматически для корректировки размера типа. Результирующее значение не n байт из источника ; вместо этого это n-й элемент массива. Дополнительные сведения об этом преобразовании см. в разделе аддитивные операторы.
Аналогично, для многомерных массивов адрес извлекается с использованием следующего метода.
Массивы в выражениях
Pascal-Паскаль
Программирование. Одномерные массивы Pascal-Паскаль
Программирование. Одномерные массивы Pascal-Паскаль
Понятие структуры
Массив – однородная совокупность элементов
Самой распространенной структурой, реализованной практически во всех языках программирования, является массив.
Другая особенность массива состоит в том, что к любой его компоненте можно обращаться произвольным образом. Что это значит? Программа может сразу получить нужный ей элемент по его порядковому номеру (индексу).
Индекс массива
Описание массива в Паскале. В языке Паскаль тип массива задается с использованием специального слова array (англ. – массив), и его объявление в программе выглядит следующим образом:
где I – тип индекса массива, T – тип его элементов.
Можно описывать сразу переменные типа массив, т.е. в разделе описания переменных:
При этом длину массива Паскаля характеризует выражение:
Вот, например, объявление двух типов: vector в виде массива Паскаля из 10 целых чисел и stroka в виде массива из 256 символов:
С помощью индекса массива можно обращаться к отдельным элементам любого массива, как к обычной переменной: можно получать значение этого элемента, отдельно присваивать ему значение, использовать его в выражениях.
Опишем переменные типа vector и stroka :
Вычисление индекса массива Паскаля
Индекс массива в Паскале не обязательно задавать в явном виде. В качестве индекса массива можно использовать переменную или выражение, соответствующее индексному типу. Иначе говоря, индексы можно вычислять.
Этот механизм – весьма мощное средство программирования. Но он порождает распространенную ошибку: результат вычислений может оказаться за пределами интервала допустимых значений индекса, то есть будет произведена попытка обратиться к элементу, которого не существует. Эта типичная ошибка называется «выход за пределы массива».
Пример программы с ошибкой массива Паскаля
Хотя данная программа полностью соответствует синтаксису языка, и транслятор «пропустит» ее, на стадии выполнения произойдет ошибка выхода за пределы массива Паскаля. При n =45 выражение n *2=90, компьютер сделает попытку обратиться к элементу массива a [90], но такого элемента нет, поскольку описан массив размерностью 80.
Будем считать, что хорошая программа должна выдавать предупреждающее сообщение в случае попытки обращения к несуществующим элементам массива. Не лишним будет проверять возможный выход как за правую, так и за левую границы массива, ведь не исключено, что в результате вычисления значения выражения получится число, находящееся левее границы массива Паскаля.
Из всего этого следует сделать вывод: программисту надо быть очень аккуратным при работе с индексами массива.
Основные действия с массивами Паскаля
Как известно, определение типа данных означает ограничение области допустимых значений, внутреннее представление в ЭВМ, а также набор допустимых операций над данными этого типа. Мы определили тип данных как массив Паскаля. Какие же операции определены над этим типом данных? Единственное действие, которое можно выполнять над массивами целиком, причем только при условии, что массивы однотипны, – это присваивание. Если в программе описаны две переменные одного типа, например,
то можно переменной a присвоить значение переменной b ( a := b ). При этом каждому элементу массива a будет присвоено соответствующее значение из массива b. Все остальные действия над массивами Паскаля производятся поэлементно (это важно!).
Ввод массива Паскаля
Для того чтобы ввести значения элементов массива, необходимо последовательно изменять значение индекса, начиная с первого до последнего, и вводить соответствующий элемент. Для реализации этих действий удобно использовать цикл с заданным числом повторений, т.е. простой арифметический цикл, где параметром цикла будет выступать переменная – индекс массива Паскаля. Значения элементов могут быть введены с клавиатуры или определены с помощью оператора присваивания.
Пример фрагмента программы ввода массива Паскаля
Рассмотрим теперь случай, когда массив Паскаля заполняется автоматически случайными числами, для этого будем использовать функцию random ( N ).
Пример фрагмента программы заполнения массива Паскаля случайными числами
Вывод массива Паскаля
Вывод массива в Паскале осуществляется также поэлементно, в цикле, где параметром выступает индекс массива, принимая последовательно все значения от первого до последнего.
Пример фрагмента программы вывода массива Паскаля
Вывод можно осуществить и в столбик с указанием соответствующего индекса. Но в таком случае нужно учитывать, что при большой размерности массива все элементы могут не поместиться на экране и будет происходить скроллинг, т.е. при заполнении всех строк экрана будет печататься очередной элемент, а верхний смещаться за пределы экрана.
Пример программы вывода массива Паскаля в столбик
На экране мы увидим, к примеру, следующие значения:
Пример решения задачи с использованием массивов Паскаля
Решение задачи:
Ход решения задачи:
Текст программы :
Пример программы суммирования векторов
Программирование
Исходники Pascal (127)
Справочник
Справочник по паскалю: директивы, функции, процедуры, операторы и модули по алфавиту
Массивы в C++
Продолжаем серию «C++, копаем вглубь». Цель этой серии — рассказать максимально подробно о разных особенностях языка, возможно довольно специальных. Это четвертая статья из серии, первые три, посвященные перегрузке в C++, находятся здесь, здесь и здесь.
Эта статья посвящена массивам. Массивы можно отнести к наиболее древним слоям C++, они пришли из первых версий C. Тем не менее, массивы вошли в объектно-ориентированную систему типов C++, хотя и с определенными оговорками. Программисту важно знать об этих особенностях, чтобы избежать потенциальных ошибок. В статье также рассмотрено другое наследие C – тривиальные типы и неинициализированные переменные. Часть нововведений C++11, С++14, С++17 затрагивают работу с массивами, все эти новые возможности также подробно описаны. Итак, попробуем рассказать о массивах все.
Оглавление
1. Общие положения
Массив является простейшим агрегатным типом. Он моделирует набор однотипных элементов, расположенных подряд в непрерывном отрезке памяти. Массивы в той или иной форме поддерживаются практически всеми языками программирования и неудивительно, что они появились в первых версиях C и затем стали частью C++.
1.1. Объявление массивов
Если T некоторый тип, N константа или выражение, вычисляемое во время компиляции, то инструкция
Такие массивы еще называют встроенными массивами (regular arrays), чтобы подчеркнуть отличие от других вариантов массивов, термин «массив» используется в программировании и в том числе в C++ достаточно широко.
Вот примеры правильных объявлений массивов:
А вот примеры некорректных объявлений массивов:
Выход за границы массива не контролируется, ошибка может привести к неопределенному поведению.
В одной инструкции можно объявить несколько массивов, но размер должен быть указан для каждого.
Для типов массивов можно вводить псевдонимы. Можно использовать традиционный вариант с ключевым словом typedef :
или более современный (C++11) с ключевым словом using :
После этого массивы объявляются как простые переменные:
Это будет то же самое, что
1.2. Операторы и стандартные функции для работы с массивами
Для работы с массивами можно использовать оператор sizeof и несколько стандартных функций и макросов.
Оператор sizeof возвращает полный размер массива в байтах, то есть размер элемента умноженный на размер массива.
А также в стандартных алгоритмах:
1.3. Размещение в памяти
Если массив объявлен статически, то есть в глобальной области видимости, в области видимости пространства имен или в качестве статического члена класса, то он размещается в статической памяти. Массивам, объявленным локально, память выделяется на стеке. (Естественно, надо учитывать ограниченный размер стека при выборе размера локальных массивов.) Нестатические члены класса размещаются в границах экземпляра класса. Динамические массивы (см. раздел 6) размещаются в динамической памяти.
1.4. Ограничения на типы элементов массивов
Нельзя объявить массив ссылок.
Вместо этого можно использовать массив константных указателей.
(Синтаксис инициализации массивов будет обсуждаться в разделе 3.2.)
Нельзя объявить массив функций.
Вместо этого можно использовать массив указателей на функцию.
Квалификатор const не применим к типу массива, а только к типам его элементов.
2. Сведение и копирование массивов
В данном разделе рассматриваются особенности массивов, которые выделяют их из общей системы типов C++.
2.1. Сведение
Конечно, тесную связь массивов и указателей отрицать нельзя. Вот стандартный (в стиле C) способ обработать все элементы массива:
Но все же сведение можно отнести к сишным архаизмам и с ним надо быть внимательным и аккуратным, иначе можно столкнуться с не самыми приятными неожиданностями.
Вот как сведение влияет на объявления функций. Функции
не являются перегруженными функциями — это одно и то же. Размер надо передавать дополнительным параметром или использовать специальное соглашение для определения размера (например, завершающий ноль для строк).
При внешнем связывании массива также происходит сведение.
Для размера также надо использовать дополнительную переменную или использовать специальное соглашение для определения размера.
При объявлении переменной с помощью ключевого слова auto также происходит сведение.
При конкретизации шаблона функции
тип параметра шаблонной функции также будет выведен как указатель, если аргумент является массивом.
Сведение вызывает дополнительные проблемы при использовании наследования. (В C ведь нет наследования.) Рассмотрим пример.
Следующий код компилируется без ошибок и предупреждений.
2.2. Копирование
Наряду со сведением (и тесно связанная с ним) есть еще одна особенность типа массива, которая делает его в некотором смысле «неполноценным». Массивы не поддерживают привычный синтаксис инициализации и присваивания, основанный на семантике копирования:
Также функция не может возвращать массив.
Но если массив является членом класса/структуры/объединения, то проблемы с копированием (а также сведение) отсутствуют.
Для этой структуры компилятор сгенерирует копирующий конструктор по умолчанию и соответствующий оператор присваивания, которые без проблем скопируют массив.
3. Инициализация массивов
Для описания правил инициализации массивов необходимо кратко рассказать о тривиальных типах.
3.1. Тривиальные типы и неинициализированные переменные
Конструкторы и деструкторы можно назвать ключевыми элементами объектной модели С++. При создании объекта обязательно вызывается конструктор, а при удалении — деструктор. Но проблемы совместимости с С вынудили сделать некоторое исключение, и это исключение называется тривиальные типы. Они введены для моделирования сишных типов и сишного жизненного цикла переменных, без обязательного вызова конструктора и деструктора. Сишный код, если он компилируется и выполняется в С++, должен работать так же как в С. К тривиальным типам относятся числовые типы, указатели, перечисления, а также классы, структуры, объединения и массивы, состоящие из тривиальных типов. Классы и структуры должны удовлетворять некоторым дополнительным условиям: отсутствие пользовательского конструктора, деструктора, копирования, присваивания, виртуальных функций.
Переменная тривиального типа будет неинициализированной, если не использовать какой-нибудь вариант явной инициализации. Для тривиального класса компилятор может сгенерировать конструктор по умолчанию и деструктор. Конструктор по умолчанию обнуляет объект, деструктор ничего не делает. Но этот конструктор будет сгенерирован и использован только, если использовать какой-нибудь вариант явной инициализации, иначе переменная останется неинициализированной.
Неинициализированная переменная устроена следующим образом: если она объявлена в области видимости пространства имен (глобально), будет иметь все биты нулевыми, если локально, или создана динамически, то получит случайный набор битов. Понятно, что использование такой переменной может привести к непредсказуемому поведению программы. Массивы достаточно часто имеют тривиальный тип и поэтому эта проблема для них весьма актуальна.
Неинициализированные константы тривиального типа выявляет компилятор, иногда он выявляет и другие неинициализированные переменные, но с этой задачей лучше справляются статические анализаторы кода.
3.2. Синтаксис инициализации массивов
3.2.1. Общие положения
Если не использовать явную инициализацию, то для массивов нетривиального типа гарантируется вызов конструктора по умолчанию для каждого элемента. Естественно, что в этом случае такой конструктор должен быть, иначе возникает ошибка. Но для массивов тривиального типа или, если конструктор по умолчанию отсутствует или не устраивает, необходимо использовать явную инициализацию.
Со времен C массивы можно было инициализировать с помощью синтаксиса агрегатной инициализации:
В С++11 появилась универсальная инициализация (uniform initialization) и теперь можно инициализировать так:
Для универсальной инициализации также можно использовать =, и различать эти два типа инициализации не всегда просто, а, скорее всего, не очень нужно.
Размер массива можно не указывать, тогда он определится по числу инициализаторов.
Если размер массива указан, то число инициализаторов не должно быть больше размера массива. Если размер массива больше числа инициализаторов, то для оставшихся элементов гарантируется вызов конструктора по умолчанию (который, естественно, должен быть), в том числе и для тривиальных типов. Таким образам, указав пустой список инициализации, мы гарантируем вызов конструктора по умолчанию для всех элементов массива тривиального типа.
Массивы констант тривиального типа требуют обязательного списка инициализации.
Число инициализаторов может быть меньше размера массива, в этом случае оставшиеся элементы инициализируются конструктором по умолчанию.
Символьные массивы можно инициализировать строковым литералом.
Размер такого массива будет на единицу больше числа символов строки, нужно хранить завершающий нулевой символ.
3.2.2. Инициализация членов класса
В С++11 появилась возможность инициализировать массивы, являющиеся нестатическими членами класса. Это можно сделать двумя способами: непосредственно при объявлении или в списке инициализации членов при определении конструктора.
Правда в этом случае надо всегда явно задавать размер массива, неявное определение размера через список инициализации не разрешается.
Статические массивы, как и ранее, можно инициализировать только при определении, размер массива может быть определен через список инициализации.
3.2.3. Требования к инициализаторам
Выражения, стоящие в списке инициализации, вычисляются непосредственно перед инициализацией, они не обязаны быть известными на стадии компиляции (конечно, за исключением массивов, объявленных как constexpr ). Требования к элементам списка инициализации такие же как и к аргументу функции, имеющей параметр того же типа, что и элемент массива — должно существовать неявное преобразование от типа элемента списка инициализации к типу элемента массива. Пусть у нас есть объявление массива:
Наличие нужного преобразования эквивалентно корректности инструкции
Элемент списка инициализации может быть сам списком инициализации. В этом случае корректность этой инструкции также гарантирует корректную инициализацию элемента массива.
Этот пример также демонстрирует как с помощью списка инициализации мы можем создать массив для типа у которого нет конструктора по умолчанию. Но в этом случае число инициализаторов должно совпадать с размером массива.
4. Указатели и ссылки на массивы
4.1. Указатели на массивы
Пусть у нас объявлен массив
Указатель на этот массив объявляется и инициализируется следующим образом:
Указатель на массив — это не указатель на первый элемент (хотя побитово они, конечно, совпадают), здесь нет никакого сведения. Это полноценный тип, который «знает» размер массива. Поэтому при инициализации размеры должны совпадать.
При инкременте указатель на массив увеличивается на размер всего массива, а не на размер элемента.
Для доступа к элементу массива через указатель надо использовать оператор * и индексатор.
При использовании псевдонимов можно получить более привычный синтаксис объявления указателя на массив.
Понимание указателей на массивы необходимо для правильной работы с многомерными массивами, которые подробно будут рассмотрены далее.
4.2. Ссылки на массивы
Пусть у нас объявлен массив
Ссылка на этот массив объявляется и инициализируется следующим образом:
Также ссылку на массив можно инициализировать разыменованным указателем на массив.
Как и указатель, ссылка «знает» размер массива. Поэтому при инициализации размеры должны совпадать.
Доступ к элементу массива через ссылку осуществляется так же, как и через идентификатор массива.
Ссылки на массивы как раз и являются теми средствами, с помощью которых можно обойти сведение.
При использовании псевдонимов можно получить более привычный синтаксис объявления ссылки на массив.
При конкретизации шаблона функции
тип параметра шаблонной функции также будет выведен как ссылка на массив, если аргумент является массивом.
Особенно удобно использовать шаблоны с выводом типа и размера массива.
5. Многомерные массивы
Если T некоторый тип, N и M выражения, допустимые для определения размера массива, то инструкция
Сведение преобразует массив к указателю на элемент. Для двумерного массива этот элемент сам является массивом, а значит двумерный массив сводится к указателю на массив.
Таким образом, при передаче двумерного массива в функцию следующие варианты объявления соответствующего параметра эквивалентны:
Это означает, что внешний размер двумерного массива теряется и его надо передавать отдельным параметром.
При использовании псевдонимов можно получить более лаконичный синтаксис объявления двумерных массивов.
Это то же самое, что
Двумерные массивы инициализируются следующим образом:
Можно получить указатель на двумерный массив:
Также можно получить ссылку. Вот пример использования ссылки на двумерный массив.
Двумерный массив хорошо согласуется с математическими матрицами. В объявлении
6. Динамические массивы
В C++ отсутствует тип «динамический массив». Имеются только операторы для создания и удаления динамического массива, доступ к нему осуществляется через указатели на начало массива (своего рода полное сведение). Размер такого массива надо хранить отдельно. Динамические массивы желательно инкапсулировать в C++ классы.
6.1. Создание и удаление динамического массива
Если T некоторый тип, n переменная, значение которой может определяются в процессе выполнения программы, то инструкция
Если тип T тривиальный, то элементы будут иметь случайное значение, в противном случае для инициализации элементов будет использован конструктор по умолчанию.
В C++11 появилась возможность использовать список инициализации.
Если число инициализаторов больше размера массива, то лишние не используются (компилятор может выдать ошибку, если значение n известно на стадии компиляции). Если размер массива больше числа инициализаторов, то для оставшихся элементов гарантируется вызов конструктора по умолчанию, в том числе и для тривиальных типов. Таким образам, указав пустой список инициализации, мы гарантируем вызов конструктора по умолчанию для всех элементов массива тривиального типа.
При этом, если при создании массива использовался конструктор, то для всех элементов массива вызывается деструктор в порядке, обратном вызову конструктора (деструктор не должен выбрасывать исключений), затем выделенная память освобождается.
6.2. Динамические массивы и интеллектуальные указатели
В C++14 появилась возможность создать динамический массив и инициализировать им экземпляр std::unique_ptr<> с помощью std::make_unique<> :
При этом гарантируется инициализация элементов массива по умолчанию, в том числе и для тривиальных типов.
Интеллектуальный указатель std::shared_ptr<> стал поддерживать такую специализацию только в C++17, а использование std::make_shared<> для этой специализации появилось только в C++20.
6.3. Многомерные динамические массивы
При использовании псевдонимов можно получить более лаконичный синтаксис.
Используя перегрузку оператора [] легко создать класс, который хранит данные в одномерном массиве, но при этом предоставляет интерфейс многомерного массива. Вот пример предельно упрощенного класса матрицы.
Вот пример использования:
7. Использование массивов в шаблонах
Тип массива можно использовать в качестве шаблонных аргументов и для специализации шаблонов классов.
В стандартной библиотеке частичная специализация интеллектуального указателя std::unique_ptr<> и std::shared_ptr<> для массивов используется для управления жизненным циклом динамического массива, подробнее см. раздел 6.2.
В качестве реального примера использования этих свойст типов приведем немного упрощенное определение перегруженного варианта шаблона функции std::make_unique<> для массивов (см. раздел 6.2):
8. Стандартные альтернативы массивам
Стандартная библиотека предоставляет несколько классов (точнее шаблонов классов), которые рекомендуется использовать вместо массивов.
Этот шаблон поддерживает индексатор и традиционный интерфейс стандартного контейнера.
Список литературы
[Josuttis]
Джосаттис, Николаи М. Стандартная библиотека C++: справочное руководство, 2-е изд.: Пер. с англ. — М.: ООО «И.Д. Вильямс», 2014.