Как сделать класс массивов

Обновлено: 04.07.2024

Содержание статьи:

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

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

  • Одномерный массив. Содержит только одну строку данных, поэтому к элементу, хранящемуся в массиве, довольно просто получить доступ с помощью одного числового индекса, ( 0, 1, 2 и т. д.)
  • Многомерный массив. Содержит более одной строки с данными, поэтому его индекс будет состоять из пары чисел, одно из которых идентифицирует строку, а другое — столбец. Такой массив часто называют прямоугольным, так как он принимает форму прямоугольника, если представить его схематично.
  • Зубчатый массив. Это массив, состоящий из подмассивов(причем эти подмассивы могут быть любого размера).

тип данных [] имя массива ;


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

Следуюший шаг — инициализируем наш массив.

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


Теперь попытаемся вывести в консоль значения элементов массива:


Этот код распечатает следующие значения :

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

В этом случае компилятор сначало посчитает количество переменных, потом определит тип, выделит необходимое количество ячеек в области оперативной памяти и проинициализирует их необходимыми значениями. При объявлении массива через new , все элементы инициализируются автоматически:

  • нулями — для цельночислового типа;
  • false — для логического;
  • null — для ссылок.

Неявная типизация массива

Определение массива объектов

Длина массива

В этом примере рассматриваемое свойство используется для ссылки на последний элемент в массиве:

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

Как мы уже упоминали ранее, для доступа к элементу массива нужно воспользоваться его порядковым номером (индексом). Например:

Не забываем, что нумерация элементов массива начинается с нуля, поэтому индекс 1-ого элемента будет 0, а четвертого — 3 ( digits[3] ). Мы изначально задали , что наш массив состоит из 4 элементов, поэтому, если обратиться, например, к шестому элементу digits[5] = 5 — получим в результате исключение IndexOutOfRangeException .

Передача массива в метод

Стандартная форма, при которой одномерный массив передается в метод выглядит так:

  • public – модификатор доступа;
  • return_type – тип, который вернул нам метод;
  • MethodName – имя метода;
  • type – тип массива, переданного в метод;
  • parameterName – название массива, являющегося одним из параметров нашего метода.

В следующем примере мы передаем массив в метод PrintArray .

Теперь все это можно соединить вместе, как показано в следующем примере:

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

В многомерном массиве каждый элемент также является массивом. Например:

Двумерный массив можно представить в виде таблицы с определенным количеством строк и столбцов.


Подмассивы и являются элементами нашего двумерного массива.

int[ , ] i= new int [2, 3];

Здесь i — это двумерный массив состоящий из двух элементов, а каждый элемент представляет собой вложенный массив из 3 элементов. Если посчитать, всего в таком массиве можно хранить 6 элементов.

Примечание: Единственная запятая в этом коде [,] означает, что массив является двумерным.

Еще мы можем указать количество строк и столбцов во время инициализации. Например:

Для доступа к элементам рассматриваемого нами массива — используем индексы. Например:


Пример 2D-массива:

В приведенном выше примере мы создали 2D-массив с элементами и .

и использовали номера индексов для доступа к элементам:

  • digits[0, 0] — доступ к первому элементу из первой строки ( 2 )
  • digits[1, 0] — доступ к первому элементу из второго ряда ( 4 )

Зубчатые массивы

Здесь у нас массив digits содержащий в себе три подмассива. Причем размерность каждого из них не совпадает, схематично образуя своеобразные зубья, за счет разной длины.

В качестве подмассивов в нем можно использовать даже многомерные массивы:


Перебор массивов (foreach)

При помощи цикла foreach мы можем перебирать элементы в любом контейнере, в том числе и в массиве. Синтаксис для его объявления такой:

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

Класс System.Array

Кроме рассмотренных, данный класс содержит около двух десятков полезных статических методов и свойств.

Резюмируем:

Что такое массивы / Одномерный массив

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

Точно так же контейнерный класс – это класс, предназначенный для хранения и организации нескольких экземпляров другого типа (либо другого класса, либо базового типа). Существует множество различных типов контейнерных классов, каждый из которых имеет различные преимущества, недостатки и ограничения в использовании. Безусловно, наиболее часто используемым контейнером в программировании является массив, примеры которого вы уже видели. Хотя C++ имеет встроенную поддержку массивов, программисты часто используют вместо него контейнерные классы массивов ( std::array или std::vector ) из-за дополнительных преимуществ, которые они предоставляют. В отличие от встроенных массивов, классы-контейнеры массивов обычно обеспечивают динамическое изменение размера (при добавлении или удалении элементов), запоминают свой размер при передаче в функции и выполняют проверку границ. Это не только делает классы-контейнеры массивов более удобными, чем обычные массивы, но и более безопасными.

Классы-контейнеры обычно реализуют довольно стандартный минимальный набор функций. Большинство хорошо определенных контейнеров будут включать функции, которые:

  • создают пустой контейнер (через конструктор);
  • вставляют новый объект в контейнер;
  • удаляют объект из контейнера;
  • сообщают количество объектов, находящихся в настоящее время в контейнере;
  • очищают контейнер от всех объектов;
  • предоставляют доступ к хранимым объектам;
  • сортируют элементы (необязательно).

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

Типы контейнеров

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

В отличие от реальной жизни, где контейнеры могут содержать любые типы объектов, которые вы в них помещаете, в C++ контейнеры обычно содержат только один тип данных. Например, если у вас есть массив чисел int , он будет содержать только числа int . В отличие от некоторых других языков, многие контейнеры C++ не позволяют произвольно смешивать типы. Если вам нужны контейнеры для хранения чисел int и double , вам, как правило, придется написать для этого два отдельных контейнера (или использовать шаблоны, что является расширенной функцией C++). Несмотря на ограничения использования, контейнеры чрезвычайно полезны и делают программирование проще, безопаснее и быстрее.

Контейнерный класс массива

В этом примере мы собираемся написать класс массива значений int с нуля, который реализует большую часть общих функций, которые должны иметь контейнеры. Этот класс массива будет контейнером значений, в котором будут храниться копии элементов. Как следует из названия, контейнер будет содержать массив чисел int , аналогичный std::vector .

Сначала создадим файл IntArray.h :

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

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

Нам также понадобятся функции, которые помогут очистить объекты IntArray . Сначала мы напишем деструктор, который просто освобождает любые динамически размещенные данные. А затем напишем функцию с именем erase() , которая сотрет массив и установит значение длины в 0.

Теперь давайте перегрузим operator[] , чтобы мы могли получить доступ к элементам массива. Мы должны проверить индекс, чтобы убедиться, что он корректен, что лучше всего сделать с помощью функции assert() . Также добавим функцию доступа для получения длины массива. Пока всё:

На данный момент у нас есть класс IntArray , который мы уже можем использовать. Мы можем размещать объекты IntArray заданного размера и можем использовать operator[] для извлечения или изменения значений элементов.

Однако есть еще кое-что, что мы не можем сделать с нашим объектом IntArray . Мы по-прежнему не можем изменить его размер, по-прежнему не можем вставлять или удалять элементы и по-прежнему не можем отсортировать его.

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

Ух! Это было немного сложно!

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

Ниже приведен наш контейнерный класс IntArray целиком.

А теперь давайте протестируем его, чтобы убедиться, что он работает:

Эта программа дает следующий результат:

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

Также стоит прямо упомянуть, что даже несмотря на то, что наш пример класса-контейнера IntArray хранит значения встроенного типа данных ( int ), мы могли бы так же легко использовать пользовательский тип (например, класс Point ).

Еще один момент: если класс из стандартной библиотеки соответствует вашим потребностям, используйте его вместо создания своего собственного. Например, вместо IntArray лучше использовать std::vector . Он протестирован в боевых условиях, эффективен и прекрасно сочетается с другими классами стандартной библиотеки. Но иногда вам может понадобиться специализированный контейнерный класс, которого нет в стандартной библиотеке, поэтому полезно знать, как создать свой собственный контейнер, когда вам это нужно. Мы поговорим о контейнерах стандартной библиотеки, когда рассмотрим еще несколько базовых тем.

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

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

Одиночные объекты классов/структур не так широко используются, как массивы объектов классов/структур. Чтобы не писать постоянно "классов/структур" и поскольку последние темы сайте (предшествующие этой, разумеется) описывают классы, буду писать просто "классы" и "массив объектов". В языке С++ структуры и классы практически одно и то же, только доступ по умолчанию отличается (приватны у классов, открытый у структур). Чтобы не говорить длинно, просто буду использовать "массив объектов".

Создание массивов

Обратите внимание, что для установки значений в массив служит метод SetValue(), а для их чтения — метод GetValue().

Копирование массивов

Поскольку массивы — это ссылочные типы, присваивание переменной типа массива другой переменной создает две переменных, ссылающихся на один и тот же массив. Для копирования массивов предусмотрена реализация массивами интерфейса ICloneable. Метод Clone(), определенный в этом интерфейсе, создает неглубокую копию массива. Если элементы массива относятся к типу значений, то все они копируются, если массив содержит элементы ссылочных типов, то сами эти элементы не копируются, а копируются лишь ссылки на них.

Вместо метода Clone() можно также применять метод Array.Сору(), тоже создающий поверхностную копию. Но между Clone() и Сору() есть одно важное отличие: Clone() создает новый массив, а Сору() требует наличия существующего массива той же размерности с достаточным количеством элементов.

Сортировка и поиск

В классе Array реализован алгоритм быстрой сортировки (Quicksort) элементов массива. Метод Sort() требует от элементов реализации интерфейса IComparable. Простые типы, такие как System.String и System.Int32, реализуют IComparable, так что можно сортировать элементы, относящиеся к этим типам.

C помощью разных вариантов метода Sort() можно отсортировать массив полностью или в заданных пределах либо отсортировать два массива, содержащих соответствующие пары "ключ-значение". После сортировки в массиве можно осуществить эффективный поиск, используя разные варианты метода BinarySearch().

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