Конструктор позволяющий создать массив размерности n

Обновлено: 04.05.2024

Для хранения прямоугольных таблиц, состоящих из строк и столбцов, необходимо использовать массивы, каждый элемент которых является строкой массивом чисел. Если считать, что одна строка — это vector , то двумерный массив — это вектор элементов типа vector , то есть его нужно объявлять как vector . При этом по стандарту языка C++ до 2011 года, в конце определения между двумя символами “

Если объявить массив a таким образом, то a[i] будет одномерным массивом, который обычно считают строкой. То есть a[i][j] будет j-м элементом i-й строки. Например, двумерный массив из 3 строк и 4 столбцов можно записать в виде:

Чтобы создать массив из n строк и m столбцов, можно объявить его указанным образом:

Затем необходмио размер “внешнего” массива изменить на n (сделать n строк в таблице):

Затем размер каждого массива-строки необходимо изменить на m. Это можно сделать циклом:

Заметим, что строки могут иметь разную длину, например, можно сделать массив в форме “лесенки”, где каждая строка будет содержать на один элемент больше предыдущей:

Но если необходимо создать прямоугольный массив, то можно сразу же при объявлении задать его размер, если воспользоваться конструктором для вектора с параметрами. Первый параметр — размер вектора, второй необязательный параметр — значение, которым будут инциализированы элементы вектора. Тогда в качестве первого параметра можно передать n, а в качестве второго параметра можно явно указать конструктор, который создает вектор из m элементов типа int : vector(m) . Итак, создать прямоугольную таблицу размером n×m можно в одну строку:

Если вложенному вызову конструктора (для строки) передать второй параметр, то все элементы массива будут заполнены переданным значением вместо нуля. int A[n] создает в памяти одномерный массив: набор пронумерованных элементов, идущих в памяти последовательно. К каждому элементу массива можно обратиться, указав один индекс - номер этого элемента. Но можно создать и двумерный массив следующим образом: int A[n][m] . Данное объявление создает массив из n объектов, каждый из которых в свою очередь является массивом типа int [m] . Тогда A[i] , где i принимает значения от 0 до n-1 будет в свою очередь одним из n созданных обычных массивов, и обратиться к элементу с номером j в этом массиве можно через A[i][j] .

Подобные объекты (массивы массивов) также называют двумерными массивами. Двумерные массивы можно представлять в виде квадратной таблицы, в которой первый индекс элемента означает номер строки, а второй индекс – номер столбца. Например, массив A[3][4] будет состоять из 12 элементов и его можно записать в виде -->

Для считывания, вывода на экран и обработки двумерных массивов необходимо использовать вложенные циклы. Первый цикл – по первому индексу (то есть по всем строкам), второй цикл – по второму индексу, то есть по всем элементам в строках (столбцам). Например, вывести на экран двумерный массив в виде таблицы, разделяя элементы в строке одним пробелом можно следующим образом:

А считать двумерный массив с клавиатуры можно при помощи еще более простого алгоритма (массив вводится по строкам, то есть в порядке, соответствующему первому примеру):

Обработка двумерного массива

Обработка двумерных массивов производится аналогичным образом. Например, если мы хотим записать в массив таблицу умножения, то есть присвоить элементу a[i][j] значение i * j , это можно сделать следующим образом при помощи вложенных циклов:

Рассмотрим более сложную задачу и несколько способов ее решения. Пусть дан квадратный двумерный массив размером n×n. Необходимо элементам, находящимся на главной диагонали проходящей из левого верхнего угла в правый нижний (то есть тем элементам a[i][j] , для которых i == j ) присвоить значение 1 , элементам, находящимся выше главной диагонали – значение 0, элементам, нахощящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n == 4 ):

Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы a[i][j] , для которых i < j , а для элементов ниже главной диагонали i >j . Таким образом, мы можем сравнивать значения i и j и по ним определять значение a[i][j] . Получаем следующий алгоритм:

Данный алгоритм плох, поскольку выполняет одну или две инструкции if для обработки каждого элемента. Если мы усложним алгоритм, то мы сможем обойтись вообще без условных инструкций.

Сначала заполним главную диагональ, для чего нам понадобится один цикл:

Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам a[i][j] для j = i+1 , . n-1 . Здесь нам понадобятся вложенные циклы:

Аналогично присваиваем значение 2 элементам a[i][j] для j = 0 , . i-1 :

Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:

Многомерные массивы

Если необходимо хранить в массиве величину, зависящую от трёх параметров-индексов, например, a[i][j][k] , то для представления такого массива нужно использовать вектор, элементами которого будут двумерные векторы, то есть

Их можно рассматривать как “трёхмерные” таблицы, но проще думать о таких массивах, просто как о величине, определяемой тремя параметрами-индексами.

Их тоже можно создавать в одну строку, добавив ещё один уровень вложенности конструкторов. Например, пусть требуется создать массив размера x×y×z, то есть первый индекс будет принимать значения от 0 до x-1, второй индекс от 0 до y-1, третий индекс от 0 до z-1. Можно использовать такое объявление:

Передача двумерных массивов в функцию

Передавать двумерные массивы в функцию всегда лучше по ссылке, т.е. добавляя знак “&” перед идентификатором параметра. Например:

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

Форматирование чисел при выводе

Допустим, мы заполним массив таблицей умножения: a[i][j] = i * j как в примере в начале раздела. Если мы теперь попробуем вывести этот массив на экран, разделяя элементы в строке одним пробелом, то из-за того, что числа имеют различную длину столбцы таблицы окажутся неровными:

Для того, чтобы получить ровные столбцы необходимо, выводить числа так, чтобы одно выводимое число имело ширину, например, ровно в 3 символа, а “лишние” позиции были бы заполнены пробелами. Тогда получится следующая таблица:

Для того, чтобы выводимое число или строка имело ровно заданную ширину, необходимо перед выводом его на экран для потока cout вызвать метод width с параметром 3 . Данный метод устанавливает ширину поля для выводимого значения. Получим следующую программу для вывода:

Заметим, что мы теперь не выводим пробел после каждого числа, поскольку мы добавили этот пробел к ширине выводимого поля. Функция width действует однократно, только на следующее выводимый в поток значение, поэтому ее нужно вызывать перед каждым выводом числа на экран.

Внимание! Если выводимое число или строка имеет большую длину, чем это было установлено функцией width , то это число или строка будут выведены полностью, а не будет обрезано до указанного значения. То есть предпочтительней вывести результат некрасиво, нежели неверно.

В каких ситуациях может возникать необходимость в таких структурах данных? Эти массивы могут применяться для представления деревьев , у которых узлы могут иметь произвольное число потомков. Таковым может быть, например, генеалогическое дерево . Вершины первого уровня - Fathers, представляющие отцов, могут задаваться одномерным массивом, так что Fathers[i] - это i-й отец. Вершины второго уровня представляются массивом массивов - Children, так что Children[i] - это массив детей i-го отца, а Children[i][j] - это j-й ребенок i-го отца. Для представления внуков понадобится третий уровень, так что GrandChildren [i][j][k] будет представлять к-го внука j-го ребенка i-го отца.

Есть некоторые особенности в объявлении и инициализации таких массивов. Если при объявлении типа многомерных массивов для указания размерности использовались запятые, то для изрезанных массивов применяется более ясная символика - совокупности пар квадратных скобок; например, int[][] задает массив , элементы которого - одномерные массивы элементов типа int .

Сложнее с созданием самих массивов и их инициализацией. Здесь нельзя вызвать конструктор new int[3][5] , поскольку он не задает изрезанный массив . Фактически нужно вызывать конструктор для каждого массива на самом нижнем уровне. В этом и состоит сложность объявления таких массивов. Начну с формального примера:

Массив jagger имеет всего два уровня. Можно считать, что у него три элемента, каждый из которых является массивом. Для каждого такого массива необходимо вызвать конструктор new , чтобы создать внутренний массив . В данном примере элементы внутренних массивов получают значение , будучи явно инициализированы константными массивами. Конечно, допустимо и такое объявление:

В этом случае элементы массива получат при инициализации нулевые значения. Реальную инициализацию нужно будет выполнять программным путем. Стоит заметить, что в конструкторе верхнего уровня константу 3 можно опустить и писать просто new int[][] . Самое забавное, что вызов этого конструктора можно вообще опустить, он будет подразумеваться:

Но вот конструкторы нижнего уровня необходимы. Еще одно важное замечание - динамические массивы возможны и здесь. В общем случае, границы на любом уровне могут быть выражениями, зависящими от переменных. Более того, допустимо, чтобы массивы на нижнем уровне были многомерными. Но это уже "от лукавого", вряд ли стоит пользоваться такими сложными структурами данных, ведь с ними предстоит еще и работать.

Приведу теперь чуть более реальный пример, описывающий простое генеалогическое дерево , которое условно назову "отцы и дети":

Здесь отцов описывает обычный динамический одномерный массив Fathers . Для описания детей этих отцов необходим уже массив массивов , который также является динамическим на верхнем уровне, поскольку число его элементов совпадает с числом элементов массива Fathers . Здесь показан еще один способ создания таких массивов. Вначале конструируется массив верхнего уровня, содержащий ссылки со значением void . А затем на нижнем уровне конструктор создает настоящие массивы в динамической памяти, с которыми и связываются ссылки.

Я не буду демонстрировать работу с генеалогическим деревом, ограничусь лишь печатью этого массива. Здесь есть несколько поучительных моментов. В классе Arrs для печати массива создан специальный метод PrintAr3 , которому в качестве аргументов передаются массивы Fathers и Children . Вот текст данной процедуры:

Приведу некоторые комментарии к этой процедуре.

  • Внешний цикл по i организован по числу элементов массива Fathers . Заметьте, здесь используется свойство Length , в отличие от ранее применяемого метода GetLength .
  • В этом цикле с тем же успехом можно было бы использовать и имя массива Children . Свойство Length для него возвращает число элементов верхнего уровня, совпадающее, как уже говорилось, с числом элементов массива Fathers .
  • Во внутреннем цикле свойство Length вызывается для каждого элемента Children[i] , который является массивом.
  • Остальные детали, надеюсь, понятны.

Приведу вывод , полученный в результате работы процедуры PrintAr3 .

Процедуры и массивы

В наших примерах массивы неоднократно передавались процедурам в качестве входных аргументов и возвращались в качестве результатов. Остается подчеркнуть только некоторые детали.

  • В процедуру достаточно передавать только сам объект - массив. Все его характеристики (размерность, границы) можно определить, используя свойства и методы этого объекта.
  • Когда массив является выходным аргументом процедуры, как аргумент C в процедуре MultMatr , выходной аргумент совсем не обязательно снабжать ключевым словом ref или out (хотя и допустимо). Передача аргумента по значению в таких ситуациях так же хороша, как и передача по ссылке. В результате вычислений меняется сам массив в динамической памяти, а ссылка на него остается постоянной. Процедура и ее вызов без ключевых слов выглядит проще, поэтому обычно они опускаются. Заметьте, в процедуре GetSizes , где определялись границы массива, ключевое слово out , сопровождающее аргументы, совершенно необходимо.
  • Функция может возвращать массив в качестве результата.

Алгоритмы и задачи

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

Последовательность элементов - - одна из любимых структур в математике. Последовательность можно рассматривать как функцию , которая по заданному значению индекса элемента возвращает его значение . Эта функция задает отображение T" />
, где - это тип элементов последовательности. В программировании последовательности это одномерные массивы, но от этого они не перестают быть менее любимыми.

Определение. Массив - это упорядоченная последовательность элементов одного типа. Порядок элементов задается с помощью индексов.

В отличие от математики, где последовательность может быть бесконечной, массивы всегда имеют конечное число элементов. Для программистов важно то, как массивы хранятся в памяти. Массивы занимают непрерывную область памяти, поэтому, зная адрес начального элемента массива, зная, сколько байтов памяти требуется для хранения одного элемента, и зная индекс (индексы) некоторого элемента, нетрудно вычислить его адрес , а значит, и хранимое по этому адресу значение элемента. На этом основана адресная арифметика в языках C и C++, где адрес элемента a(i) задается адресным выражением a+i , в котором имя массива a воспринимается как адрес первого элемента. При вычислении адреса i-го элемента индекс i умножается на длину слова, требуемого для хранения элементов типа T. Адресная арифметика использует 0-базируемость элементов массива, полагая индекс первого элемента равным нулю, поскольку первому элементу соответствует адресное выражение а+0 .

Ввод-вывод массивов

Как у массивов появляются значения, как они изменяются? Возможны три основных способа:

  • вычисление значений в программе;
  • значения вводит пользователь;
  • связывание с источником данных.

В задачах этого раздела ограничимся пока рассмотрением первых двух способов. Первый способ более или менее понятен. Простые примеры его применения приводились неоднократно. Стоит только отметить, что в классе, работающем с массивами, всегда полезно иметь метод FillArray , позволяющий заполнять массив случайными числами. В примерах использование возможностей класса Random для моделирования элементов массива встречалось неоднократно.

Приведу некоторые рекомендации по вводу и выводу массивов, ориентированные на работу с конечным пользователем.

Для консольных приложений ввод массива обычно проходит несколько этапов:

  • ввод размеров массива;
  • создание массива;
  • организация цикла по числу элементов массива, в теле которого выполняется:
    • приглашение к вводу очередного элемента;
    • ввод элемента;
    • проверка корректности введенного значения.

    Вначале у пользователя запрашиваются размеры массива, затем создается массив заданного размера. В цикле по числу элементов организуется ввод значений. Вводу каждого значения предшествует приглашение к вводу с указанием типа вводимого значения, а при необходимости - и диапазона, в котором должно находиться требуемое значение. Поскольку ввод значений - это ответственная операция, а на пользователя никогда нельзя положиться, после ввода часто организуется проверка корректности введенного значения. При некорректном задании значения элемента ввод повторяется, пока не будет достигнут желаемый результат.

    При выводе массива на консоль обычно вначале выводится имя массива, а затем его элементы в виде пары: = (например, f[5] = 77,7). Задача осложняется для многомерных массивов, когда пользователю важно видеть не только значения, но и структуру массива, располагая строку массива в строке экрана.

    Как организовать контроль ввода? Наиболее разумно использовать для этих целей конструкцию охраняемых блоков - try - catch блоков. Это общий подход, когда все опасные действия, связанные с работой пользователя, внешних устройств, внешних источников данных, размещаются в охраняемых блоках .

    Как правило, для ввода-вывода массивов пишутся специальные процедуры, вызываемые в нужный момент.

    Ввод-вывод массивов в Windows-приложениях

    Приложения Windows позволяют построить дружелюбный интерфейс пользователя, облегчающий работу по вводу и выводу массивов. И здесь, когда данные задаются пользователем, заполнение массива проходит через те же этапы, что рассматривались для консольных приложений. Но выглядит все это более красиво, наглядно и понятно. Пример подобного интерфейса, обеспечивающего работу по вводу и выводу одномерного массива, показан на рис. 6.4.

    Для облегчения работы пользователя выводится подсказка, какой именно элемент должен вводить пользователь. После того, как все элементы массива введены, окно ввода становится недоступным для ввода элементов. Интерфейс формы позволяет многократно создавать новый массив, повторяя весь процесс.

    На рис. 6.4 форма разделена на две части - для ввода и вывода массива. Крайне важно уметь организовать ввод массива, принимая данные от пользователя. Не менее важно уметь отображать существующий массив в форме, удобной для восприятия пользователя. На рисунке показаны три различных элемента управления, пригодные для этих целей, - ListBox, CheckedListBox и ComboBox. Как только вводится очередной элемент, он немедленно отображается во всех трех списках.

    В реальности отображать массив в трех списках, конечно, не нужно, это сделано только в целях демонстрации возможностей различных элементов управления. Для целей вывода подходит любой из них, выбор зависит от контекста и предпочтений пользователя. Элемент ComboBox имеет дополнительное текстовое окно, в которое пользователь может вводить значение. Элемент CheckedListBox обладает дополнительными свойствами в сравнении с элементом ListBox, позволяя отмечать некоторые элементы списка (массива). Отмеченные пользователем элементы составляют специальную коллекцию. Эта коллекция доступна, с ней можно работать, что иногда весьма полезно. Чаще всего для вывода массива используется элемент ListBox.

    Посмотрим, как это все организовано программно. Начну с полей формы OneDimArrayForm , показанной на рис. 6.4:

    Полями этого класса является одномерный массив, его размер, текущий индекс и константы, используемые в процессе диалога с пользователем. Обработчик события Click командной кнопки, отвечающей за создание массива, имеет вид:

    Функция GetItem вводит значение очередного элемента. Если пользователь корректно задал его значение, то элемент добавляется в массив, а заодно и в списки, отображающие текущее состояние массива. Создается подсказка для ввода следующего элемента массива, а если массив полностью определен, то форма переходит в состояние окончания ввода.

    Форму OneDimArrayForm можно рассматривать как некоторый шаблон, полезный при организации ввода и вывода одномерных массивов.

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

    Ошибка возникает здесь: "long long int Body[*p]". Я пробовал несколько раз добавлять слова const и static к определению p (Так как компилятор ругается на то, что это не const значение или нестатическая ссылка), но на самом деле не понимаю как работает static.

    Начнем с того, что вы хотите указать в качестве размера массива указатель. Далее - все же размер класса должен быть определен во время компиляции, так что вам следует в конструкторе выделять память для массива динамически, а в деструкторе - ее освобождать.

    @Harry с указателям это я действительно загнул. Вы сказали, что "размер класса должен быть определен во время компиляции". Можно ли как нибудь обойти это ограничение? Я бы не хотел выделять память под массив, если существует альтернативное решение, так как отладка с выделенной памятью сложнее по сравнению с обычным массивом.

    Если бы такое было возможно, то каждый обьект класса мог бы иметь разный размер. Представте что было бы. Ужас

    1 ответ 1

    Если хотите хранить в классе именно массив, то нужно указать размер этого массива во время опознания типа(во время компиляции). В конструкторе же вы можете лишь указать размер, который предположительно будет использоваться, а остальная часть нужна для запаса(мало ли что. ). Экземпляры класса должны иметь возможность выдачи информации о размерах и о начале и конце массива, а также о количестве запаса(неиспользованных элементов). Пример:

    Если не акцентировать внимание на то, что есть некоторые недостатки, класс вполне удовлетворят ваши намерения, а если нет, то и не нужно хранить массивы (тем более, что лучше занимать память в стеке на один указатель, чем на целый массив, может быть, очень большой длины). В общем случаи, лучше пользоваться стандартными контейнерами и адаптерами контейнеров.

    Спасибо, вам большое! Но не могли бы объяснить пару строк? "bigestint(const size_t R) : length(R % (SIZE + 1)) <>" - begestin() - конструктор класса, а что такое ':' ? и что означает length()? разве это не переменная? "size_t free_place() const < return SIZE - length; >" - Я не совсем понимаю смысл const в данном случаи.

    если вы обратили внимание, то класс имеет два поля _ массив Body и размер массива length. В языке есть возможность инициализировать поля в списке инициализаторов конструктора. Метка ':' и есть начало спика инициализации, после него до тела конструктора приводите список конструкторов. : а(k), b(n) и т.д. <> что означает, что 'a' конструируется со значением 'k', 'b' конструируется со значением 'n'. В данном случаи наш обьект, хранящий размер _ length конструируется со значением R % (SIZE + 1), чтобы по любому это значение не было больше чем SIZE .

    Далее length это имя переменной(обьекта), который должен создаваться, и если вы сами не указываете с каким значением создать обьект, то обьект будет содержать мусор, пока вы не выполните присваивание. Для константных обьектов нельзя вызвать неконстантные методы, поэтому те методы, которые не модифицирует обьект, нужно объявлять константными, чтобы вызвать и для константных обьектов. К тому же модификатор сразу дает понять, что метод только для чтения. Если все понятно, и вам удовлетворил ответ, нажмите на галочку. Если ответ не понравился _ на стрелочку для указания на не полезность

    Как правильно объявить массив объектов, чтоб для каждого элемента вызвался конструктор? В C++ аналогичная конструкция работает как надо, и конструктор вызывается.

    @Grundy можно, только она будет инициализироваться дефолтными значениями типов, иначе говоря нулями и null-значениями. Так что все равно придется пробегать по массиву и инициировать внутреннюю логику, если она есть.

    @rdorn, забавно, что хотели добавить возможность объявлять конструктор без параметров для структур, но передумали :)

    4 ответа 4

    Тут вы создаете сам объект класса MyClass

    А тут вы создаете массив ссылок на объекты класса MyClass

    Что бы создать массив объектов класса MyClass вам требуется создать каждый объект собственноручно:

    более строго не "массив класса MyClass", а массив ссылок на объекты MyClass. Поэтому и требуется ручное создание объектов. В cpp при тех же условиях будет создан массив объектов, т.е. в ячейках массива будут храниться сами объекты.

    Никак. Массив - это отдельный объект. То есть грубо говоря мы создаем не "Пять объектов типа MyClass". А: "Один объект, размером в пять объектов MyClass". Как следствие создавать объекты при конструировании массива нельзя. Но объекты можно создавать после создания массива, Например в инициализаторе:

    Либо через "for" (как в соседнем ответе).

    "Один объект, размером в пять объектов MyClass" в корне не верно, если MyClass не является структурой. Размер массива для ссылочных типов не зависит от размера объектов и не содержит самих объектов.

    Ответ AGS17 жизнеспособен, но можно также воспользоваться и обобщенным методом:

    Использовать можно так:

    В чем профит использования этого метода? Экономия 2 строчек кода? Он же (метод) сделает код чуточку медленней (или заинлайнится?), требует чуточку памяти (что не критично), и делает код чуточку менее понятней (читателю надо будет пройти и поглядеть, что же там происходит внутри метода), ещё и название метода не совсем верное, так как тут не только инициализация массива, но и элементов. Метод ещё и работает только с типами с дефолтным конструктором. По мне так вещь бесполезная.

    Этот метод при активном использовании будет работать значительно медленнее, чем при использовании цикла - new T() в генериках разворачивается в рефлекшн, Activator.CreateInstance, который значительно медленнее обычного new.

    Массив — это последовательность объектов того же типа, которые занимают непрерывную область памяти. Традиционные массивы в стиле C являются источником многих ошибок, но по-прежнему являются распространенными, особенно в старых базах кода. В современном C++ настоятельно рекомендуется использовать std::vector или std::array вместо массивов в стиле C, описанных в этом разделе. Оба этих стандартных типа библиотек хранят свои элементы в виде непрерывного блока памяти. Однако они обеспечивают гораздо большую безопасность типов и поддерживают итераторы, которые гарантированно указывают на допустимое расположение в последовательности. Дополнительные сведения см. в разделе "Контейнеры".

    Объявления стека

    В объявлении массива C++ размер массива указывается после имени переменной, а не после имени типа, как в некоторых других языках. В следующем примере для стека объявляется массив из 1000 двойников. Число элементов должно быть предоставлено в виде целочисленного литерала или в виде константного выражения. Это связано с тем, что компилятору необходимо знать, сколько пространства стека необходимо выделить; Он не может использовать значение, вычисленное во время выполнения. Каждому элементу в массиве присваивается значение по умолчанию 0. Если вы не назначаете значение по умолчанию, каждый элемент изначально содержит любые случайные значения, которые будут находиться в этом расположении памяти.

    Первый элемент в массиве является нулевым элементом. Последним элементом является элемент (n-1), где n — количество элементов, которые может содержать массив. Число элементов в объявлении должно быть целочисленным типом и должно быть больше 0. Ваша ответственность заключается в том, чтобы программа никогда не передает значение оператору подстрока, который больше (size - 1) .

    Массив нулевого размера является допустимым только в том случае, если массив является последним полем в struct или union если расширения Майкрософт включены ( /Za или /permissive- не заданы).

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

    Объявления кучи

    Может потребоваться слишком большой массив для выделения в стеке или размер которого неизвестлен во время компиляции. Этот массив можно выделить в куче с помощью new[] выражения. Оператор возвращает указатель на первый элемент. Оператор подстрока работает с переменной указателя так же, как и в массиве на основе стека. Можно также использовать арифметические указатели для перемещения указателя на любые произвольные элементы в массиве. Вы несете ответственность за обеспечение следующего:

    • Всегда хранится копия исходного адреса указателя, чтобы можно было удалить память, если массив больше не нужен.
    • Адрес указателя не увеличивается или уменьшается после границ массива.

    В следующем примере показано, как определить массив в куче во время выполнения. В нем показано, как получить доступ к элементам массива с помощью оператора подстрока и с помощью арифметики указателя:

    Инициализация массивов

    Массив можно инициализировать в цикле, по одному элементу за раз или в одной инструкции. Содержимое следующих двух массивов идентично:

    Передача массивов в функции

    Когда массив передается функции, он передается в качестве указателя на первый элемент, будь то массив на основе стека или кучи. Указатель не содержит дополнительных сведений о размере или типе. Такое поведение называется разложением указателя. При передаче массива в функцию необходимо всегда указывать количество элементов в отдельном параметре. Это также означает, что элементы массива не копируются при передаче массива в функцию. Чтобы предотвратить изменение элементов функцией, укажите параметр в качестве указателя на const элементы.

    В следующем примере показана функция, которая принимает массив и длину. Указатель указывает на исходный массив, а не копию. Так как параметр не const задан, функция может изменять элементы массива.

    Объявите и определите параметр p массива так, const чтобы он был доступен только для чтения в блоке функции:

    Эту же функцию также можно объявить таким образом, без каких-то изменений в поведении. Массив по-прежнему передается в качестве указателя на первый элемент:

    Многомерные массивы

    Массивы, созданные из других массивов, являются многомерными. Такие многомерные массивы определяются путем последовательного размещения нескольких константных выражений, заключенных в квадратные скобки. Рассмотрим, например, следующее объявление:

    Он задает массив типа int , концептуально упорядоченный в двумерной матрице из пяти строк и семи столбцов, как показано на следующем рисунке:


    Концептуальная структура многомерного массива

    Можно объявить многомерные массивы с списком инициализаторов (как описано в инициализаторах). В этих объявлениях можно опустить константное выражение, указывающее границы для первого измерения. Пример:

    В показанном выше объявлении определяется массив, состоящий из трех строк и четырех столбцов. Строки представляют фабрики, а столбцы — рынки, на которые фабрики поставляют свою продукцию. Значения — это стоимости транспортировки с фабрик на рынки. Первое измерение массива опущено, но компилятор заполняет его, проверяя инициализатор.

    Использование оператора косвенного обращения (*) в типе n-мерного массива приводит к получению массива n-1. Если n равно 1, создается скаляр (или элемент массива).

    Массивы C++ размещаются в памяти по срокам. Построчный порядок означает, что быстрее всего изменяется последний индекс.

    Пример

    Можно также опустить спецификацию границ для первого измерения многомерного массива в объявлениях функций, как показано ниже:

    Эта функция FindMinToMkt записывается таким образом, что добавление новых фабрик не требует каких-либо изменений в коде, а просто перекомпиляции.

    Инициализация массивов

    Массивы объектов с конструктором класса инициализируются конструктором. Если в списке инициализатора меньше элементов, чем элементов в массиве, конструктор по умолчанию используется для остальных элементов. Если для класса не определен конструктор по умолчанию, список инициализаторов должен быть завершен, то есть для каждого элемента в массиве должен быть один инициализатор.

    Рассмотрим класс Point , определяющий два конструктора:

    Первый элемент aPoint создается с помощью конструктора Point( int, int ) , а оставшиеся два элемента — с помощью конструктора по умолчанию.

    Статические массивы элементов (независимо от того, можно ли const ) инициализировать в их определениях (за пределами объявления класса). Пример:

    Доступ к элементам массива

    К отдельным элементам массива можно обращаться при помощи оператора индекса массива ( [ ] ). Если вы используете имя одномерного массива без подстрока, оно вычисляется как указатель на первый элемент массива.

    Если используются многомерные массивы, в выражениях можно использовать различные сочетания.

    В приведенном выше коде multi представляет собой трехмерный массив типа double . Указатель p2multi указывает на массив типа double размера 3. В этом примере массив используется с одним, двумя и тремя индексами. Хотя обычно указываются все подстроки, как в инструкции cout , иногда бывает полезно выбрать определенное подмножество элементов массива, как показано в следующих cout инструкциях.

    Перегруженный оператор подстрочного индекса

    Как и другие операторы, оператор подстрока ( [] ) может быть переопределен пользователем. Поведение оператора индекса по умолчанию, если он не перегружен, — совмещать имя массива и индекс с помощью следующего метода.

    Как и во всех дополнениях, включающих типы указателей, масштабирование выполняется автоматически для настройки размера типа. Результирующая величина не является n байтами из источника array_name ; вместо этого это n-й элемент массива. Дополнительные сведения об этом преобразовании см. в разделе "Аддитивные операторы".

    Аналогично, для многомерных массивов адрес извлекается с использованием следующего метода.

    ((array_name) + (subscript1 * max2 * max3 * . * maxn) + (subscript2 * max3 * . * maxn) + . + subscriptn))

    Массивы в выражениях

    Если идентификатор типа массива отображается в выражении, отличном от sizeof адреса ( & ) или инициализации ссылки, он преобразуется в указатель на первый элемент массива. Пример:

    Указатель psz указывает на первый элемент массива szError1 . Массивы, в отличие от указателей, не являются изменяемыми l-значениями. Вот почему следующее назначение является незаконным:

    Читайте также: