246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 57
Текст из файла (страница 57)
Следующий цикл for (строки 26—30) обращается к каждомуобъек- ту,являющемусяэлементоммассива,ивызываетдлявыбранногообъектаметодGetAge().Чтобы применить метод GetAge() для объекта, являющегося элементом массива,используютсяпоследовательнооператорыиндексации([])ипрямогодоступакчленукласса(.),атакжевызовфункции-члена.МногомерныемассивыМожно создать и использовать массив, содержащий более одного измерения. Доступ ккаждому измерению открывается своим индексом. Так, чтобы получить доступ к элементудвухмерного массива, нужно указать два индекса; к элементу трехмерного массива — трииндекса и т.д.
Теоретически можно создать массив любой мерности, но, как правило, впрограммахиспользуютсяодномерныеидвухмерныемассивы.Хорошимпримернымдвухмерногомассиваявляетсяшахматнаядоска,состоящаяизклеток,собранныхввосемьрядовивосемьстолбцов(рис.12.3).Рис.12.3.ШахматнаядоскаидвухмерныймассивПредположим,чтовпрограммеобъявленклассSQUARE.ОбъявлениедвухмерногомассиваBoardдлясохраненияобъектовэтогоклассабудетвыглядетьследующимобразом:SQUAREBoard[8][8];Этижеобъектыможнобылосохранитьводномерноммассивес64элементами:SGUAREBoard[64];Использованиедвухмерногомассиваможетоказатьсяпредпочтительнее,еслитакоймассивлучшеотражаетположениевещейвреальноммире,напримерприсозданиипрограммыигрывшахматы.
Так, в начале игры король занимает четвертую позицию в первом ряду. С учетомнулевогосдвигапозицияэтойфигурыбудетпредставленаобъектоммассива:Board[0][3];В этом примере предполагается, что первый индекс будет контролировать нумерациюрядов, а второй — столбцов. Соответствие элементов массива клеткам шахматной доскинагляднопоказанонарис.12.3.ИнициализациямногомерногомассиваМногомерный массив также можно инициализировать одновременно с объявлением. Приэтом следует учитывать, что сначала весь цикл значений проходит индекс, указанныйпоследним,послечегоизменяетсяпредпоследнийиндекс.Такимобразом,еслиестьмассивinttheArray[5][3]:то первые три значения будут записаны в массив theArray[0], вторые три значения — вмассивtheArray[1]ит.д.Указанныймассивможноинициализироватьследующейстрокой:inttheArray[5][3]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};Чтобынезапутатьсявчислах,значенияможносгруппироватьспомошьюдополнительныхфигурныхскобок,например:inttheArray[5][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12},{13,14,15}};Компилятор проигнорирует все внутренние фигурные скобки.
Все значения должны бытьразделенызапятыминезависимооттого,используетевыдополнительныефигурныескобкиилинет. Весь список значений должен быть заключен во внешние фигурные скобки, и послезакрывающейскобкиобязательноустанавливаетсясимволточкисзапятой.Пример создания двухмерного массива показан в листинге 12.5. Первый ряд двухмерногомассива содержит целые числа от 0 до 4, а второй — удвоенные значения соответствующихэлементовпервогоряда.Листинг12.5.Созданиемногомерногомассива1:#include<iostream.h>2:intmain()3:{4:intSomeArray[5][2]={{0,0},{1,2},{2,4},{3,6},{4,8}}5:for(intt=0;i<5;i++)6:for(intj=0;j<2;j++)7:{8:cout<<"SomeArray["<<i<<"]["<<j<<"]:";9:cout<<SomeArray[i][j]<<endl;10:}11:12:return0;13:}Реультат:SomeArray[0][0]: 0 ' '' " SomeArray[0][1]: 0 SomeArray[1][0]: 1 SomeArray[1][1]: 2SomeArray[2][0]: 2 SomeArray[2][1]: 4 SomeArray[3][0]: 3 SomeArray[3][1]: 6 SomeArray[4][0]: 4SomeArray[4][1]:8Анализ: В строке 4 объявляется двухмерный массив.
Первый ряд содержит пятьцелочисленных значений, а второй ряд представлен двумя значениями. В результате создаетсяконструкцияиздесятиэлементов(5x2),какпоказанонарис.12.4.Рис.12.4.Массив5x2Данные вводятся в массив попарно, хотя их можно было записать одной строкой. Затемосуществляется вывод данных с помощью двух вложенных циклов for. Внешний циклпоследовательно генерирует индексы первого ряда, а внутренний — индексы второго ряда.
Втакой последовательности данные выводятся на экран: сначала идет элемент SomeArray[0][0],затем элемент SomeArray[0][1]. Приращение индекса первого ряда происходит после того, какиндекс второго ряда становится равным 1, после чего вновь дважды выполняется внутреннийцикл.НесколькословопамятиПри объявлении массива компилятору точно указывается, сколько объектов планируется внемсохранить.Компиляторзарезервируетпамятьдлявсехобъектовмассива,дажееслидалеевпрограмме они не будут заданы. Если вы заранее точно знаете, сколько элементов долженхранить массив, то никаких проблем не возникнет.
Например, шахматная доска всегда имееттолько 64 клетки, а от кошки можно ожидать, что она не родит более 10 котят. Если жеизначально неизвестно, сколько элементов будет в массиве, то для решения этой проблемынужноиспользоватьболеегибкиесредствауправленияпамятью.В этой книге рассматриваются только некоторые дополнительные средствапрограммирования,такиекакмассивыуказателей,массивысрезервированиемпамятивобластидинамического обмена и ряд других возможностей.
Больше информации о средствахпрограммирования,открывающихдополнительныевозможности,можнопрочитатьвмоейкнигеC++ Unleashed, выпущенной издательством Sams Publishing. И вообще, всегда следует помнить,что каким бы хорошим программистом вы ни были, всегда остается то, чему следовало бынаучиться,ивсегдаестьисточники,откудаможнопочерпнутьновуюсвежуюинформацию.МассивыуказателейВсемассивы,рассмотренныенамидосихпор,хранилизначениясвоихэлементоввстекахпамяти. Использование стековой памяти связано с рядом ограничений, которых можноизбежать, если обратиться к более гибкой области динамической памяти. Это можно сделать,еслисначаласохранитьвсеобъектымассивавобластидинамическойпамяти,азатемсобратьвмассивеуказателинаэтиобъекты.Этотподходзначительносократитпотреблениепрограммойстековойпамятикомпьютера.Влистинге12.6показантотжемассив,скоторыммыработаливлистинге 12.4, но теперь все его объекты сохранены в области динамической памяти.
Чтобыпоказатьвозросшуюэффективностьиспользованияпамятипрограммой,вэтомпримереразмермассивабылувеличенс5до500иегоназваниеизмененосLitter(помет)наFamily(семья).Листинг12.6.Сохранениемассивавобластидинамическойпамяти1://Листинг12.6.Массивуказателейнаобьекты2:3:#include<iostream.h>4:5:classCAT6:{7:public:8:CAT(){itsAge=1;itsWeight=5;}9:~CAT(){}//destructor10:intGetAge()const{returnitsAge;}11:intGetWeight()const{returnitsWeight:}12:voidSetAge(intage)(itsAge=age;}13:14:private:15:intitsAge;16:intitsWeight;17:};18:19:intmain()20:{21:CAT*Family[500];22:inti;23:CAT*pCat;24:for(i=0;i<500;i++)25:{26:pCat=newCAT;27:pCat->SetAge(2*i+1);28:Family[i]=pCat;29:}30:31:for(i=0;i<500;i++)32:{33:cout<<"Cat#"<<i+1<<":";34:cout<<Family[i]->GetAge()<<endl;35:}36:return0;37:}Результат:Cat#1:1Cat#2:3Cat#3:5...Cat#499:997Cat#500:999Анализ:ОбъявлениеклассаCATвстроках5—17идентичнообъявлениюэтогоклас-•caвлистинге12.4.Но,вотличиеотпредыдущеголистинга,встроке21объявляется массив Family, в котором можно сохранить 500 указателей на объекты классаCAT.В цикле инициализации (строки 24-29) в области динамической памяти создается 500новых объектов класса CAT, каждому из которых присваивается значение переменной itsAge,равноеудвоенномузначениюиндексаплюсодин.Такимобразом,первомуобъектуклассаCATприсваиваетсязначение1,второму—3,третьему—5ит.д.Вэтомжециклекаждомуэлементумассиваприсваиваетсяуказательнавновьсозданныйобъект.ПосколькутипмассивабылобъявленкакCAT*,внемсохраняютсяименноуказатели,анеихразыменованныезначения.Следующий цикл (строки 31—35) выводит на экран все значения объектов, на которыеделаютсяссылкивмассиве.Обращениекуказателювыполняетсяспомощьюиндекса:Family[i].Послетогокакэлементмассиваустановлен,следуетвызовметодаGetAge().Вданномпримерепрограммывсеэлементымассивасохраняютсявстековойпамяти.Новэтот раз элементами являются указатели, тогда как сами объекты хранятся в областидинамическогообмена.ОбъявлениемассивоввобластидинамическогообменаСуществует возможность поместить весь массив в область динамического обмена.
Дляэтогоиспользуетсяключевоесловоnewиоператориндексирования,какпоказановследующемпримере, где результатом этой операции является указатель на массив, сохраненный в областидинамическогообмена:CAT*Family=newCAT[500];УказательFamilyбудетсодержатьадресвдинамическойобластипервогоэлементамассиваиз пятисот объектов класса CAT. Другими словами, в указателе представлен адрес объектаFamily[0].Еще одно преимущество подобного объявления массива состоит в том, что в программе спеременной Family теперь можно будет выполнять математические действия как с любымдругим указателем, что открывает дополнительные возможности в управлении доступом кэлементаммассива.Например,можновыполнитьследующиедействия:CAT*Family=newCAT[500];CAT*pCat=Family;//pCatуказываетнаFamily[0]pCat->SetAge(10);//присваиваетFamily[0]значение10pCat++;//переходкFamily[1]pCat->SetAge(20);//присваиваетFamily[1]значение20В данном примере объявляется новый массив из 500 объектов класса CAT и возвращаетсяуказатель на первый элемент этого массива.
Затем, используя это указатель и метод SetAge(),объявленныйвклассеCAT,первомуобъектумассиваприсваиваетсязначение 10. Переход к следующему объекту массива осуществляется за счет приращенияадресавуказателенамассив,послечеготемжеспособомприсваиваетсязначение20второмуобъектумассива.УказательнамассивилимассивуказателейРассмотримследующиетриобъявления:1:CatFamily0ne[500];2:CAT>>FamilyTwo[500];3:CAT*FamilyThree=newCAT[500];В первом случае объявляется массив FamilyOne, содержащий 500 объектов типа CAT.
Вовтором случае — массив FamilyTwo, содержащий 500 указателей на объекты класса CAT, и втретьемслучае—указательFamilyThree,ссылающийсянамассивиз500объектовклассаCAT.В зависимости от того, какое объявление используется в программе, принципиальноменяются способы управления массивом. Как ни странно, но указатель FamilyThree по сутисвоейгораздоближекмассивуFamilyOne,нопринципиальноотличаетсяотмассивауказателейFamilyTwo.Чтобы разобраться в этом, следует внимательно рассмотреть, что содержат в себе все этипеременные.