Глава 14
В этой главе...
Массив — это определенный способ представления данных, используемый во многих программах. Строение массива похоже на строку (или столбец) ячеек электронной таблицы: в вашем распоряжении есть целое множество отдельных ячеек, в которые
можно записывать информацию. Такая структура очень удобна для сохранения данных и их последующей обработки.
В этой главе описывается использование массива для сохранения списка элементов, содержащих данные о графических объектах. Также вы познакомитесь со специальными классами .NET, которые предназначены для оптимизации процесса сохранения данных в программе.
Вверх
Особенность массивов состоит в том, что каждый их элемент имеет порядковый номер, называемый индексом. Индекс используется для получения доступа к данным, сохраненным в каждом отдельном элементе. Индекс позволяет также использовать циклы для просмотра значений всех элементов массива или значений только некоторого диапазона элементов. С помощью индекса можно сразу же получить непосредственный доступ к данным любого элемента массива. Такой способ доступа к данным называется произвольным; он намного быстрее использования связанных списков.
Предположим, вы используете массив для хранения информации о продолжительности
Прежде чем создавать массив, вы должны определить, из какого количества элементов он будет состоять. В этом отличие массивов от списков, где количество элементов заранее не известно. (Хотим вас обрадовать: далее в главе рассматривается специальный класс .NET ArrayList, который объединяет в себе все лучшие свойства массивов и списков.)
Предположим, например, что anFoo — это массив чисел типа integer. Тогда он может
Индекс Значение
Как видите, первый элемент массива имеет нулевой индекс, второй — индекс 1 и т.д. И в данном случае элемент с индексом 0 имеет значение 32, а злемент с индексом 4 — значение 10.
Совет:
Чтобы создать массив, нужно просто указать тип данных, имя массива и в квадратных скобках ([]) количество элементов.
//Создание массиза типа integer, состоящего из 20
Помните, что первый элемент массива имеет нулевой индекс. Поэтому, если массив имеет n
Те, кто только начинает использовать массивы, очень часто забывают об этом и думают, что первый элемент имеет индекс 1, а потом долго не могут понять, почему они получают из массива не те значения, которые им нужны.
Аналогичную ошибку совершают и в отношение последнего элемента массива: при обращении к нему вместо индекса
(
n - 1) используют индекс n. В результате этого либо будут получены совершенно нечитаемые данные, либо возникнет ошибка GPF (общее нарушение защиты). Последнее случается из-за того, что C++ не может предотвратить обращение к памяти, расположенной за пределами массива. Если вернуться к приведенному выше коду и предположить, что вы ошибочно набрали anFoo [20] , это будет обращение к участку памяти, расположенному сразу после области памяти, выделенной для элементов данного массива. Последствия могут быть самыми непредсказуемыми.
Вверх
Чтобы получить доступ к значению какого-либо элемента массива, наберите имя массива и укажите в квадратных скобках индекс этого элемента. Продемонстрируем это на примере.
//Массив типа integer из 20 элементов
//а второму значение 3
//Отображение значения второго элемента
//Отображение значения третьего элемента, умноженного на 5
Обратите внимание, что при отображении только элементов массива не нужно
Вверх
Инициализация — это присвоение начальных значений. Инициализировать массив можно
Другой способ — использовать цикл. Этот путь очень удобен, если можно использовать какой-то шаблон для присвоения значений или если эти значения могут быть получены из файла данных. Например, если необходимо, чтобы массив содержал значения от 1 до 20, наберите такой код:
//Цикл, на каждой итерации которого очередному
Еще один способ инициализировать массив — указать значения элементов при объявлении этого массива. При этом можно указывать значение не всех элементов, а только некоторых (остальным элементам, для которых вы значения не укажете, автоматически будут присвоены нули). Например, массив может быть инициализирован таким образом:
В данном случае первым шести элементам будут присвоены перечисленные значения
В своей программе вы можете также создать и инициализировать массив, состоящий из текстовых значений. В этом случае при объявлении массива его размер указывать не нужно.
Массив элементов со значениями типа х, по сути, является указателем на значение типа х. Поэтому, набранное вами
Единственная разница в том, что когда вы объявляете массив, выделяется память сразу для множества значений указанного типа, в то время как при объявлении указателя память выделяется только для одного элемента. Переменная массива ссылается на первый элемент этого же массива.
Указатели иногда используются для получения доступа к элементам массива. Рассмотрим это на таком примере:
Если вы используете указатель для ссылки на значение элемента массива, при добавлении единицы в действительности добавляется размер памяти, выделенный для одного элемента этого массива. Таким образом вы получаете адрес следующего элемента.
Неуправляемый код
Вверх
Массивы, которые рассматривались до сих пор, были одномерными. Кроме них, существуют также многомерные массивы, без использования которых вряд ли можно обойтись при решении многих типичных и не очень типичных задач. Представим, например, что вам нужно сохранить информацию о том, сколько домов обозначено в каждом квадрате на карте города.
Рис. 14.1. Квадраты обычной двухмерной карты представлены ячейками двухмерного массива, значения которых соответствуют количеству обозначенных на карте домов
Или возьмем другой пример: нужно смоделировать перемещение какого-нибудь летающе
Чтобы определить многомерный массив нужно использовать столько квадратных скобок ([]), сколько размерностей будет иметь этот массив. Следующим кодом, например, создается двухмерный массив, представляющий шахматную доску (восемь клеток в ширину и восемь в длину):
Чтобы обратиться к нужной ячейке, опять-таки необходимо использовать столько квадратных скобок, сколько размерностей имеет массив:
Вверх
Самостоятельно создавать массивы — это здорово. Не менее увлекательное занятие — создавать собственные связанные списки (что было продемонстрировано в главе 13). Но если вы не в духе или просто не хочется напрягаться, среда .NET предлагает вам воспользоваться специально разработанными классами для представления структур данных. В действительности это даже лучше, чем создавать массивы и списки самостоятельно. Пускай Microsoft выполнит за вас, всю рутинную работу, а также найдет и исправит все возможные стандартные ошибки. Вы же в
это время можете заняться более важными делами и создать ошибки посерьезнее.
Одна из лучших структур .NET носит название ArrayList. Она похожа на массив в том смысле, что к ее элементам можно обращаться непосредственно (т.е. они имеют порядковые номера), и в то же время подобна списку, поскольку на количество ее элементов нет ограничений (т.е. в любой момент в конец этой структуры может быть добавлен новый элемент).
"Многомерные" советы
Ниже приведено несколько советов, которые могут вам пригодиться при создании и использовании многомерных массивов.
Вот как создается структура ArrayList:
А вот как созданная структура заполняется информацией:
//Добавление указателя
Совет:
Единственной сложностью, которая имеет место при использовании структуры
ArrayList (а также при использовании структуры Stack, с которой вы вскоре познакомитесь), является переход от одного элемента к другому. Предположим, например, что структура ArrayList состоит из пяти элементов и вам нужно просмотреть значения ее элементов, начиная с первого и далее. Можно создать цикл, который будет использовать для доступа к значениям элементов метод get_Itera. Или можете использовать более сложный прием, который, однако, отлично работает со всеми классами .NET, представляющими структуры данных, включая те, которые не упоминаются на страницах этой книги. Этот более сложный прием заключается в создании нумератора (его еще называют перечислителем), название которого расшифровывается приблизительно как "специальная "штуковина", позволяющая по порядку, один за другим, просматривать значения элементов структуры".
Вот как создается нумератор:
После того как нумератор создан, можете использовать любой из методов, приведенных в табл. 14.1. Если, например, вам нужно поочередно отобразить значения всех элементов структуры ArrayList, наберите такой код:
Этим кодом вначале создается нумератор, а затем компилятор, переходя от одного элемента к другому, пока не будет достигнут конец структуры, отображает значения этих элементов. Обратите внимание, что метод MoveNext, выполняющий переход к следующему элементу, должен быть вызван до отображения значения первого элемента.
Таблица 14.1. Методы, используемые при работе с нумератором
Массивы: познакомимся поближе
музыкальных записей. Тогда, если нужно просмотреть, например, продолжительность первой записи, вы просто смотрите значение элемента под номером 1. Точно так же можно сохранять информацию о котировках акций на биржах, данные о сотрудниках, результаты футбольных матчей и т.д.
выглядеть приблизительно так:
0 32
1 10
2 17
3 -5
4 10
Например, чтобы создать массив типа integer, наберите такой код:
//элементов (индексы от 0 до 19)
int anFoo[20];
элементов, последний элемент будет иметь индекс (n - 1). Например,
для массива, показанного несколькими абзацами выше, n равнялось числу 5
(поскольку массив состоит из пяти элементов). Первый элемент имеет нулевой
индекс, а последний — индекс 4 (или 5-1).
Это же «элементарно», Ватсон
int anFoo[20];
//Первому элементу присваивается значение 20,
anFoo[0] = 20;
anFoo[l] = 3;
Console::WriteLine(anFoo[1]);
Console::WriteLine(S*anFoo[2]);
вызывать функцию ToString для преобразования числового значения в текстовое.
Среда .NET может это сделать автоматически. Если же вы используете более
сложные варианты функции Console::WriteLine, например форматируете
строки, вызвать функцию ToString все же придется.
Инициализация, массива
несколькими способами. Можно, например, вручную присвоить значения каждому элементу:
anFoo[0] = 1;
anFoo[l] = 3;
//Массив из 20 элементов
Int anIntegers [20] ;
//элементу присваивается значение,
//большее значения предыдущего элемента на 1
for (int i = 0; i < 20; i++)
anIntegers [i] = i + 1;
int anMyInts[10] = {1, 3, 5, 9, 4, 8};
(первому элементу— 1, второму— 3 и т.д.), а последние четыре элемента получат значения, равные нулю.
Например:
//Создание массива, состоящего из строк
String *aszFoo[] =
{S"hello", S"goodbye"};
Console::WriteLine(S"B массиве есть слово
{0}", asz Foo [1]);
Связь между массивами и указателями :
int foo[8];
будет почти аналогично набранному
int *foo;
//Массив из 8 элементов с числовыми значениями
int foo
[8];
//Указатель на числовое значение
int *bar;
//Указателю присваивается адрес первого элемента массива
bar = foo;
//Отображение значения первого элемента массива
cout << *bar;
//Отображение значения второго элемента массива
//Обратите внимание, что это равнозначно foo[1]
cout << * (bar + 1) ;
иначе. Если вы помните, сама строка — это массив символов (объявляется как
char *). Ниже приведен код неуправляемой программы, при выполнении кото-
рого создается и инициализируется массив из строк, а затем все значения этого
массива отображаются на экране.
//Создание массива из трех строк и присвоение его
//элементам начальных значений
char *aszFoo[3] =
{"hello", "goodbye", "bye-bye"};
//Отображение строк на экране
cout << aszFoo[0] << aszFoo[l] << aszFoo[2] << endl;
Многомерные массивы
Поскольку карта двухмерна, для решения этой проблемы удобнее и логичнее использовать двухмерный массив (рис. 14.1).
го насекомого в пределах вашей комнаты. Для этого можно условно разбить пространство комнаты на кубики, соизмеримые с размерами животного, и отмечать его перемещение из кубика в кубик. Множество кубиков будет представлять собой трехмерный массив. Многомерные массивы могут быть использованы при решении не только "пространственных" задач. Например матрицы, которые применяются для обработки графики, являются двухмерными массивами. Управление большими объемами разнообразных данных может быть сведено к работе с многомерными массивами.
int anChessBoard[8][8];
//Получение значения из ячейки 3,4
anFull = anChessBoard[3][4];
Класс ArrayList
Более того, элементы этой структуры могут содержать значения любых типов, для которых активизирована возможность сборки мусора (garbage collection).
ArrayList *poArray = new ArrayList ( ) ;
//Добавление строки
Можно также присвоить элементу индекс, а потом получать доступ к
poArray->Add(5"Строка") ;
pcArray->Acid (poFirst) ;
его значению, ссылаясь на этот индекс:
poArray->set_Itern (О, S" Hello");
Console::WriteLine(poArray->get_Itern(0));
значения элемента, убедитесь, что этот индекс не ссылается за пределы созданной
структуры.
Структура ArrayList обладает некоторыми весьма полезными возможностями. Например, можно определить, какой из элементов структуры имеет то или иное конкретное значение:
Console::WriteLine(poArray->IndexOi~(S"Значение"));
IEnumerator *poEnumerator ~ poArray->GetEnumerator();
IEnumerator *poEnumerator - poArray->GetEnumerator();
while ( poEnumerator->MoveNext() )
Console::WriteLine( poEnumerator->Current );
Метод | Пример | Описание |
Current | poE->Current | Возвращает значение элемента, на который указывает нумератор |
MoveNext | poE->MoveNext() | Изменяет значение нумератора с тем, чтобы он указывал на следующий элемент структуры. Если элементов больше нет, возвращает логическое значение false |
Reset | poE->Reset() | Нумератор возвращается к первому элементу структуры |
Вверх
Другая замечательная структура .NET, используемая для представления данных, называется Stack. Принцип ее работы очень простой. Представьте, что вы собираетесь в поход и
Итак, последний элемент, записанный в Stack, извлекается из него первым. Как и в случае со структурой ArrayList, в Stack можно записывать данные любых типов, для которых активизирована возможность сборки мусора.
Чтобы создать Stack, наберите такой код:
Чтобы извлечь элемент из структуры Stack, наберите:
Переход от одного элемента к другому осуществляется тем же способом, который использовался для просмотра элементов структуры ArrayList:
while ( poEnumerator->MoveNext() )
Console::WriteLine( poEnumerator->Current );
Вверх
вы могли бы набрать:
Любое слово, указанное в списке enum (Верхняя, Средняя или Нижняя), может быть
Безопасность при использовании перечислимых типов
Если хотите снизить вероятность возникновения ошибок при написании программы, укажите, что набор элементов, перечисленных в списке enum, является отдельным типом данных. Таким образом будет исключена возможность случайного использования констант из
одного набора enum (созданного, скажем, для представления названий музыкальных записей) в тех выражениях, где должны использоваться константы enum из другого набора (представляющего, например, типы моторных масел).
Можно, например, указать, что константы, обозначающие способ размещение вещей в
//Использование списка констант enum в качестве
//Присвоение переменной значения из списка enum
По возможности старайтесь обозначать списки констант enum как перечислимые
типы данных. Тогда, если вы по ошибке попытаетесь использовать константу перечислимого типа для обозначения информации не того вида, компилятор выдаст
предупреждение. Программа из-за этого работать не перестанет, но предупреждающее сообщение поможет вам определить источник возникновения этой ошибки и вовремя ее устранить.
Технические подробности:
Например, приведенный ниже код вызовет сообщение об ошибке, суть которого будет
приблизительно в том, что оператор не может определить, как правильно обрабатывать значения типа enum Размещение.
Назад |
Начало урока |
Вверх |
Вперед
Класс Stack
вам нужно сложить вещи в рюкзак. Вначале разумнее положить то, что нужно будет доставать в последнюю очередь, затем можно положить какие-то другие вещи, а на самый верх то, что придется доставать в первую очередь. Когда вы будете вынимать вещи из рюкзака, вы вначале достанете то, что положили туда последним, потом другие вещи и наконец ту вещь, которую клали самой первой.
Stack *poStack = new Stack();
Следующим кодом элемент помещается в Stack:
poStack->Push(3"Какая-нибудь строка");
poStack->Pop();
poEnumerator = poStack->GetEnumerator();
Перечислимые Типы
Вы только что узнали о том, как можно использовать нумератор для просмотра элементов, содержащихся в структурах ArrayList и stack. Но есть еще одна разновидность нумераторов (или перечислителей), которая обозначается термином перечислимый тип. Перечислимые типы удобны в тех случаях, когда есть набор элементов, но, вместо того чтобы
идентифицировать их по числовым константам, вы хотите поставить им в соответствие какие-нибудь легкочитаемые понятные обозначения. Например, вещи в рюкзаке можно обозначить словами Верхняя, средняя и Нижняя. Первый вариант: сказать, что 0— это вещь, положенная сверху, 1 — вещь, положенная посередине, и 2 — вещь, положенная на самый низ. Второй вариант: создать перечислимый тип.
Так, например, вместо кода
const int Верхняя = 0;
const int Средняя - 1;
const int Нижняя = 2 ;
enum
{Верхняя, Средняя, Нижняя};
использовано в любом месте программы в качестве константы.
рюкзаке, принадлежат типу Размещение:
enum Размещение
{Верхняя, Средняя, Нижняя};
Если вы поступите таким образом, компилятор сможет проверять, действительно ли переменная, для которой определен тип Размещение, принимает только те значения, которые для этого типа предусмотрены. Продемонстрируем это на небольшом примере:
enum Shapes
{
Square
//типа данных
Shapes oMyShape;
//Обратите внимание, что код oMyShape = Oval будет
//воспринят как синтаксическая ошибка
oMyShape = Circle ;
Одно маленькое "но"
тот факт, что команда cin может считывать значения только стандартных
(предустановленных) типов данных. Если вы попытаетесь использовать команду
cin для получения от пользователя значения, указанного в списке enum, компилятор выдаст сообшение об ошибке.
//Создание перечислимого типа
enum Размещение
{Верхняя, Средняя, Нижняя };
//Переменная foo типа Размещение
Размещение foo;
//Пользователь должен указать значение для foo
cin >> foo;
Содержание