246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 58
Текст из файла (страница 58)
Указатель на массив FamilyThree содержит адрес первого элемента массива, новедьэтоименното,чтосодержитимямассиваFamilyOne.ИменамассивовиуказателейВC++имямассивапредставляетсобойконстантныйуказательнапервыйэлементмассива.Другимисловами,вобъявленииCATFamily[50];создаетсяуказательFamilyнаадреспервогоэлементамассива&Family[0].В программе допускается использование имен массивов как константных указателей инаоборот.
Таким образом, выражению Family + 4 соответствует обращение к пятому элементумассиваFamily[4].Компилятор выполняет с именами массивов те же математические действия сложения,инкрементаидекремента,чтоисуказателями.ВрезультатеоперацияFamily+4будетозначатьне прибавление четырех байтов к текущему адресу, а сдвиг на четыре объекта. Если размеродногообъектаравенчетырембайтам,токадресувименимассивабудутдобавленыне4,а16байт. Если в нашем примере каждый объект класса CAT содержит четыре переменные-членатипа long по четыре байта каждая и две переменные-члена типа short по два байта каждая, торазмер одного элемента массива будет равен 20 байт и операция Family + 4 сдвинет адрес вимениуказателяна80байт.Объявление массива в динамической области памяти и его использование показано влистинге12.7.Листинг12.7.Созданиемассивасиспользованиемключевогословаnew1://Листинг12.7.Массиввдинамическойобластипамяти2:3:#include<iostream.h>4:5:classCAT6:{7:public:8:CAT(){itsAge=1;itsWeight=5;}9:~CAT();10:intGetAge()const{returnitsAge;}11:intGetWeight()const{returnitsWeight;}12:voidSetAge(intage){itsAge=age;}13:14:private:15:intitsAgo;16:intitsWeight;17:};18:19:CAT::~CAT()20:{21://cout<<"Destructorcalled!\n";22:}23:24:intmain()25:{26:CAT*Family=newCAT[500];27:inti;28:29:for(i=0;i<500;i++)30:{31:Family[i].SetAge(2*i+1);32:}33:34:for(i=0;i<500;i++)35:{36:cout<<"Cat#"<<i+1<<":";37:cout<<Family[i].GetAge()<<endl;38:}39:40:delete[]Family;41:42:return0;43:}Результат:Cat#1:1Cat#2:3Cat#3:5...Cat#499:997Cat#500:999Анализ: В строке 26 объявляется массив Family для пятисот объектов класса CAT.Благодаря использованию выражения new CAT[500] весь массив сохраняется в областидинамическойпамяти.УдалениемассиваизобластидинамическойпамятиКуда деваются при удалении массива все эти объекты класса CAT, показанные впредыдущем разделе? Не происходит ли здесь утечка памяти? Удаление массива Family спомощью оператора delete[ ] (не забудьте установить квадратные скобки) освобождает всеячейки памяти, отведенные для него.
Компилятор достаточно сообразительный, чтобы удалитьиз памяти все объекты удаляемого массива и освободить динамическую память для новогоиспользования.Чтобыубедитьсявэтом,изменитеразмермассивавпредыдущейпрограммес500на10встроках26,29и34.Затемразблокируйтевыражениевстроке21соператоромcoutизапуститепрограмму. Когда будет выполнена строка 43, последует десять вызовов деструктора дляудалениякаждогообъектаклассаCATвмассивеFamily.Создавая какой-либо объект в области динамической памяти с помощью ключевого словаnew, всегда удаляйте его из памяти с помощью оператора delete, если этот объект больше неиспользуется в программе. В случае создания массива в области динамического обменавыражением new <class>[size] удалять его из памяти нужно оператором delete[].
Квадратныескобкиуказывают,чтоудаляетсявесьмассив.Есливызабудетеустановитьквадратныескобки,тоизпамятибудетудалентолькопервыйобъектмассива.Вэтомможноубедиться,есливнашемпримерепрограммыудалитьквадратныескобки в строке 38. Если уже были внесены изменения в строку 21, как указывалось выше, топри выполнении программы на экране отобразится вызов только одного деструктора объекта,которыйудалитпервыйобъектмассива.Поздравляемвас!Выпотерялиогромныйблокпамятидлядальнейшегоиспользованияпрограммой.Рекомендуется:Помните, что для обращения к массиву из n элементов используютсяиндексы от 0 до n-1.
Используйте свойства математических операций с указателями дляуправлениядоступомкэлементаммассива.Не рекомендуется:Не записывайте данные за пределы массива. Не путайте массивуказателейсуказателемнамассив.МассивысимволовСтрока текста представляет собой набор символов. Все строки, которые до сих пориспользовались нами в программах, представляли собой безымянные строковые константы,используемыеввыраженияхсоператоромcout,такиекак:cout<<"helloworld.\n";Но в C++ строку можно представить как массив символов, заканчивающийся кон- цевьшнулевымсимволомстроки.Такоймассивможнообъявитьиинициализироватьточнотакже,каклюбойдругоймассив,например:charGreeting[]={'H','e','1','Г,'o','','W','o',1r','1','d',1\0'};В последний элемент массива заносится нулевой концевой символ строки (\0), которыймногиефункцииC++распознаюткаксимволразрывастроки.Хотяметодвводастрокитекставмассив символ за символом работает нормально, это довольно утомительная процедура,чреватаяошибками.Ксчастью,C++допускаетупрощенныйметодвводастроктекставмассивы:charGreeting[]="helloworld";Обратитевниманиенадвамоментаданногосинтаксиса.• Вместо одиночных кавычек вокруг каждого символа, запятых между символами ифигурныхскобоквокругвсейстрокивданномпримереиспользуютсятолькодвойныекавычкивокруг строки и ничего более.
Нет даже обычных для инициализации массивов фигурныхскобок.•Нетнеобходимостидобавлятьконцевойнулевойсимвол,таккаккомпиляторсделаетэтоавтоматически.СтрокаHelloWorldзаймет12байт.ПятьбайтовпойдетнасловоHello,пятьнасловоWorldипоодномубайтунапробеликонцевойнулевойсимвол.Инициализацию строкового массива можно оставить на потом. При этом, также как и сдругимимассивами,нужноследить,чтобызатемвмассивнебылозаписаносимволовбольше,чемотводилосьдляэтогоместа.В листинге 12.8 показан пример использования массива символов, которыйинициализируетсястрокой,вводимойпользователемсклавиатуры.Листинг12.8.Заполнениемассивасимволами1://Листинг12.8.Заполнениемассивасимволами2:3:#include<iostream.h>4:5:intmain()6:{7:charbuffer[80];8:cout<<"Enterthestring:";9:cin>>buffer;10:cout<<"Hereis'thebuffer:"<<buffer<<endl;11:return0;12:}Результат:Enterthestring:HelloWorldHere'sthebuffer:HelloАнализ:Встроке7объявляетсямассивbuffer,рассчитанныйна80символов.Taкоймассивможетсодержатьстрокуиз79букв,включаяпробелы,плюснулевойконцевойсимволстроки.В строке 8 пользователю предлагается ввести строку текста, которая копируется в массивbufferвстроке9.Методcinавтоматическидобавитнулевойконцевойсимволвконцевведеннойстроки.Но при выполнении программы, показанной в листинге 12.8, возникает ряд проблем.
Вопервых,еслипользовательвведетстроку,содержащуюболее79символов,тооператорcinвведетихзапределамимассива,Во-вторых,операторcinвоспринимаетпробелкакокончаниестроки,послечегопрекращаетвводданных.Чтобы решить эти проблемы, нужно использовать метод get(), применяемый вместе соператоромcin:cin,get().Длявыполненияметоданужнозадатьтрипараметра.•Буферввода.•Максимальноечислосимволов.•Разделительныйсимволпрерыванияввода.Поумолчаниювкачестверазделительногозадаетсясимволразрывастроки.Использованиеэтогометодапоказановлистинге12.9.Листинг12.9.Заполнениемассива1://Листинг12.9.Использованиеметодаcin.get()2:3:#include<iostream.h>4:5:intmain()6:{7:charbuffer[80];8:cout<<"Enterthestring:";9:cin.get(buffer,79);//вводзавершаетсяпосле79символаилисимволаразрывастроки10:cout<<"Here'sthebuffer:"<<buffer<<endl;11:return0;12:}Результат:Enterthestring:HelloWorldHere'sthebuffer:HelloWorldАнализ:Встроке9осуществляетсявызовметодаcin.get().Буферввода,заданныйвстроке7, передается в функцию как первый аргумент.
Второй аргумент задает максимальную длинустроки, равную 79 символам. Допускается ввод только 79 символов, поскольку последнийэлементмассиваотводитсянаконцевойнулевойсимволстроки.Устанавливатьтретийаргументне обязательно. В большинстве случаев в качестве разделительного символа подходитзадаваемыйпоумолчаниюсимволразрывастроки.Функцииstrcpy()иstrncpy()ЯзыкC++унаследовалотСбиблиотечныефункции,выполняющиеоперациинадстроками.Среди множества доступных функций есть две, которые осуществляют копирование однойстрокивдругую.Этофункцииstrcpy()иstrncpy().Функцияstrcpy()копируетстрокуцеликомвуказанныйбуфер,какпоказановлистинге12.10.Листинг12.10.Использованиефункцииstrcpy()1:#include<iostream.h>2:#include<string.h>3:intmain()4:{5:charString1[]="Nomanisanisland";6:charString2[80];7:8:strcpy(String2,String1);9:10:cout<<"String1:"<<String1<<endl;11:cout<<"String2:"<<String2<<endl;12:return0;13:}Результат:String1:NomanisanislandString2:NomanisanislandАнализ: Файл заголовка string.h включается в программу в строке 2.
Этот файл содержитпрототипфункцииstrcpy().Вкачествеаргументовфункцииуказываютсядвамассивасимволов,первый из которых является целевым, а второй — массивом источника данных. Если массивисточник окажется больше целевого массива, то функция strcpy() введетданные за пределымассива.Чтобы предупредить подобную ошибку, в этой библиотеке функций содержится еще однафункция копирования строк: strncpy(). Эта функция копирует ряд символов, не превышающийдлины строки, заданной в целевом массиве. Функция strncpy() также прерывает копирование,если ей повстречается символ разрыва строки. Использование функции strncpy() показано влистинге12.11.Листинг12.11.Использованиефункцииstrncpy()1:#include<iostream.h>2:#include<string.h>3:intmain()4:{5:constintMaxLength=80;6:charString1[]="Nomanisanisland";7:charString2[MaxLength+1];8:9:10:strncpy(String2,String1,MaxLength);11:12:cout<<"String1:"<<String1<<endl;13:cout<<"String2:"<<String2<<endl;14:return0;15:}Результат:String1:NomanisanislandString2:NomanisanislandАнализ: В строке 10 программа вместо функции strcpy() используется функцию strncpy(),третий параметр MaxLength которой задает максимальную длину копируемой строки.