246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 38
Текст из файла (страница 38)
Приобъявлении указателя pLong (строка 13) была зарезервирована область памяти, на которуюраньше ссылался указатель pInt. Заметим, что на некоторых компьютерах этого могло непроизойти. Записывая число 20 по адресу, на который до этого указывал pInt, мы искажаемзначение pLong, хранящееся по этому же адресу. Вот к чему может привести некорректноеиспользованиеблуждающихуказателей.Локализоватьтакуюошибкудостаточносложно,посколькуискаженноезначениеникакнесвязано с блуждающим указателем. Результатом некорректного использования указателя pIntстало изменение значения pLong.
В больших программах отследить возникновение подобнойситуацииособенносложно.Вкачественебольшогоотступлениярассмотрим,какпоадресувуказателеpLongоказалосьчисло65556.1.УказательpIntссылаетсянаобластьпамяти,вкоторуюмызаписаличисло10.2. Оператором delete мы позволяем компилятору использовать эту память для хранениядругихданных.ДалеепоэтомужеадресузаписываетсязначениеpLong.3. Переменной *pLong присваивается значение 90000. Поскольку в компьютереиспользовалосьчетырехбайтовоепредставлениетипаlongсперестановкойбайтов,намашинномуровнечисло90000(00015F90)выгляделокак5F900001.4.
Затем указателю pInt присваивается значение 20, что эквивалентно 00 14 вшестнадцатеричнойсистеме.Вследствиетогочтоуказателиссылаютсянаоднуитужеобластьпамяти,двапервыхбайтачисла90000перезаписываются.ВрезультатеполучаемЧИСЛО00140001.5.ПривыводенаэкранзначениявуказателеpLongпорядокбайтовизменяетсяна00010014,чтоэквивалентночислу65556.Рекомендуется:Создавайтеобъектывобластидинамическогообмена.Применяйтеоператорdeleteдляосвобожденияобластейдинамическогообмена,которыебольшенеиспользуются.Проверяйтезначения,возвращаемыеоператоромnew.Не рекомендуется:Не забывайте каждое выделение свободной памяти с помощьюоператора new сопроводить освобождением памяти с помощью оператора delete.Незабывайтеприсваиватьосвобожденнымуказателямнулевыезначения.Вопросыиответы:Какаяразницамеждупустымиблуждающимуказателями?Когдавыприменяетекуказателюоператорdelete,освобождаетсяобластьдинамическойпамяти; на которую ссылался этот указатель, но сам указатель продолжает при этомсуществовать,становясьблуждающим.Присваиваяуказателюнулевоезначение,напримерследующимвыражением:myPtr-0:,вытемсамымпревращаетеблуждающийуказательвнулевой.Ещеоднаопасностьблуждающихуказателей состоит в том, что, дважды применив к одному и тому же указателю операторdelete, вы тем самым создадите неопределенную ситуацию, которая может привести кзависанию программы.
Этого не случится, если освобожденному указателю будет присвоенонулевоезначение.Присвоениеосвобожденномууказателю—какблуждающему,такинулевому—ново-гозначения(т.е.использованиевыраженияmyPtr=5)недопустимо,ноесливслучаеспустымуказателемобэтомвамсообщиткомпилятор,товслучаесблуждающимуказателемвыузнаетеобэтомпозависаниюпрограммывсамыйнеподходящиймомент.ИспользованиеключевогословаconstприобъявленииуказателейПри объявлении указателей допускается использование ключевого слова const передспецификаторомтипаилипосленего.Корректны,например,следующиевариантыобъявления:constint*pOne;int*constpTwo;constint*constpThree;В этом примере pOne является указателем на константу типа int. Поэтому значение, накотороеонуказывает,изменятьнельзя.Указатель pTwo является константным указателем на тип int.
В этом случае значение,записанноепоадресувуказателе,можетизменяться,носамадресостаетсянеизменным.Инаконец,pThreeобъявленкакконстантныйуказательнаконстантутипаint.Этоозначает,чтоонвсегдауказываетнаоднуитужеобластьпамятиизначение,записанноепоэтомуадресу,неможетизменяться.В первую очередь необходимо понимать, какое именно значение объявляется константой.Если наименование типа переменной записано после ключевого слова const, значит,объявляемая переменная будет константой.
Если же за словом const следует имя переменной,константойявляетсяуказатель.constint*p1;//Укаэатольнакоисттпутипаiniint*constp2;//Константныйуказаюль,всегдауказывающийнаоднуитужеобластьпамятиИспользованиеключевогословаconstприобъявленииуказателейифункций-членовИспользованиеключевогошваconstприобъявленииуказателейифункции-членовНазанятии4мыобсудиливопрособиспользованииключевогословаconstприобъявлениифункций-членов классов. При объявлении функции константной попытка внести изменения вданныеобъектаспомощьюэтойфункциибудутпресекатьсякомпилятором.Если указатель на объект объявлен константным, он может использоваться для вызоватолькотехметодов,которыетакжеобъявленысоспецификаторомconst(листинг8.10).Листинг8.10.Указателинаконстантныеобъекты1://Листинг8.10.2://Вызовконстантныхметодовспомощьюуказателей3:4:flinclude<iostream.h>5:6:classRectangle7:{8:public:9:Rectangle();10:~Rectangle();11:voidSetLength(intlength){itsLength=length;}12:intGetLength()const{returnitsLength;}13:voidSetWidth(intwidth){itsWidth=width:}14:intGetWidth()const{returnitsWidth;}15:16:private:17:intitsLength;18:intitsWidth;19:};20:21:Rectangle::Rectangle()22:{23:itsWidth=5;24:itsLength=10;25:}26:27:Rectangle::~Rectangle()28:{}29:30:intmain()31:{32:Rectangle*pRect=newRectangle;33:constRectangle*pConstRect=newRectangle;34:Rectangle*constpConstPtr=newRectangle;35:36:cout<<"pRectwidth;"<<pRect->GetWidth()<<"meters\n";37:cout<<"pConstRectwidth:"<<pConstRect->GetWidth()<<"meters\n";38:cout<<"pConstPtrwidth:"<<pConstPtr->GetWidth()<<"meters\n";39:40:pRect->SetWidth(10);41://pConstRect->SetWidth(10);42:pConstPtr->SetWidth(10);43:44:cout<<"pRectwidth:"<<pRect->GetWidth()<<"meters\n";45:cout<<"pConstRectwidth:"<<pConstRect->GetWidth()<<"meters\n";46:cout<<"pConstPtrwidth:"<<pConstPtr->GetWidth()<<"meters\n";47:return0;48:}Результат:pRectwidth:5meterspConstRectwidth:5meterspConstPtrwidth:5meterspRectwidth:10meterspConstRectwidth:5meterspConstPtrwidth:10metersАнализ: В строках 6—19 приведено описание класса Rectangle.
Обратите внимание, чтометод GetWidth(), описанный в строке 14, имеет спецификатор const. Затем в строке 32объявляетсяуказательнаобъектклассаRectangle,австроке33—наконстантныйобъектэтогожекласса.КонстантныйуказательpConstPrtописываетсявстроке34.Встроках36—38значенияпеременныхклассавыводятсянаэкран.Метод SetWidth(), вызванный для указателя pRect (строка 40), устанавливает значениеширины объекта. В строке 41 показан пример использования указателя pConstRect для вызоваметодакласса.Но,таккакpConstRectявляетсяуказателемнаконстантныйобъект,вызовметодовбез спецификатора const для него недоступен, поэтому данная строка закомментирована.
Встроке 42 происходит вызов метода SetWidth() для указателя pConstPrt. Этот указательконстантныйиможетссылатьсятольконаоднуобластьпамяти,однакосамобъектконстантнымнеявляется,поэтомуданнаяоперацияполностьюкорректна.Рекомендуется:Проверяйтезначения,возвращаемыефункциейmalloc().Защищайтеобъекты,которыенедолжныизменятьсявпрограмме,спомощьюключевогословаconstвслучаепередачиихкакссылок.Передавайте как ссылки те объекты, которые должны изменяться в программе.Передавайтекакзначениянебольшиеобъекты,которыенедолжныизменятьсявпрограмме.УказательconstthisПосле объявлении константного объекта указатель this также будет использоваться какконстантный. Следует отметить, что использование указателя const this допускается только вметодах,объявленныхсоспецификаторомconst.Болееподробноэтотвопросрассматриваетсянаследующемзанятииприизученииссылокнаконстантныеобъекты.ВычислениясуказателямиОдин указатель можно вычитать из другого.
Если, например, два указателя ссылаются наразные элементы массива, вычитание одного указателя из другого позволяет получитьколичествоэлементовмассива,находящихсямеждудвумязаданными.Наиболееэффективноэтаметодикаиспользуетсяприобработкесимвольныхмассивов(листинг8.11).Листинг8.11.Выделениесловизмассивасимволов1:#include<iostream.h>2:#include<ctype.h>3:#include<string.h>4:boolGetWord(char*string,char*word,int&wordOffset);5://основнаяпрограмма6:intmain()7:{8:constintbufferSize=255;9:charbuffer[bufferSize+1];//переменнаядляхранениявсейстроки10:charword[bufferSize+1];//переменнаядляхраненияслова11:intwordOffset=0;//начинаемспервогосимвола12:13:cout<<"Enterаstring:";14:cin.getline(buffer,bufferSize);15:16:while(GetWord(buffer,word,wordOffset))17:{18:cout<<"Gotthisword:"<<word<<endl;19:}20:21:return0;22:23:}24:25:26://Функциядлявыделениясловаизстрокисимволов.27:boolGetWord(char*string,char*word,int&wordOffset)28:{29:30:if(!string[wordOffset])//определяетконецстроки?31:returnfalse;32:33:char*p1,*p2;34:p1=p2=string+wordOffset;//указательнаследующееслово35:36://удаляемведущиепробелы37:for(inti=0;i<(int)strlen(p1)&&!isalnum(p1[0]);i++)38:p1++;39:40://проверканаличияслова41:if(!iKalruj[n(pl[0]))42:returnfalse;43:44://указательр1показаниеначалосдолующегослова45://iакжокакиp246:p2=p1;47:48://перпмещавмp2иконецолова49:while(isalnum(p2[0]))50:p2++;51:62://p2указываетнаконецслова53://аp1-вначало54://разностьуказатолойпоказываотдлинуслова55:intlen=int(p2-p1);56:57://копируемслововбуфер58:strncpy(word,p1,len);59:60://идобавляемсимволразрывасроки61:word[len]='\0';62:63://ищемначалоследующегослова64:for(inti=int(p2-string);K(int)strlen(string)&&!isalnum(p2[0]);i++)65:p2++;66:67:wordOffset=int(p2-string);68:69:returntrue;70:}Результат:Enterаstring:thiscodefirstappearedjnC++ReportGotthisword:thisGotthisword:codeGotthisword:firstGotthisword:appearedGotthisword:inGotthisword:CGotthisword:ReportАнализ: В строке 13 пользователю предлагается ввести строку.