246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 71
Текст из файла (страница 71)
В строке 70выбранный метод вызывается для созданного объекта посредством предоставления доступа кобъектуспомощьюуказателяptrикфункцииспомощьюуказателяpFunc.Наконец,строкой71дляуказателяptrвызываетсяфункцияdelete,котораяочищаетобластьпамяти,занятуюсозданнымранееобъектом.Заметьте,чтонетсмыславызыватьdeleteдляpFunc,поскольку последний является указателем на код, а не на объект в области памяти. Хотя дажеприпопыткесделатьэтовыполучитесообщениеобошибкекомпиляции.Массивыуказателейнафункции-членыАналогичноуказателямнаобычныефункции,указателинафункции-членымогутхранитьсявмассиве.Дляинициализациитакогомассиваможноиспользоватьадресаразличныхфункцийчленов.
В таком случае, чтобы вызвать для объекта тот или иной метод, достаточно простоуказатьмассивииндекссмещения.Именнотакойподходприменяетсявлистинге14.11.Листинг14.11.Массивуказателейнафункции-члены1://Листинг14.11.Массивыуказателейнафункции-члены2:3:#include<iostream.h>4:5:classDog6:{7:public:8:voidSpeak()const{cout<<"Woof!\n";}9:voidMove()const{cout<<"Walkingtoheel...\n";}10:voidEat()const{cout<<"Gobblingfood...\n";}11:voidGrowl()const{cout<<"Grrrrr\n";}12:voidWhimper()const{cout<<"Whiningnoises...\n";}13:voidRollOver()const{cout<<"Rollingover...\n";}14:voidPlayDead()const{cout<<"IsthistheendofLittleCaesar?\n";15:};16:17:typedefvoid(Dog::*PDF)()const;18:intmain()19:{20:constintMaxFuncs=7;21:PDFDogFunctions[MaxFuncs]=22:{Dog::Speak,23:Dog::Move,24:Dog::Eat,25:Dog::Growl,26:Dog::Whimper,27:Dog::RollOver,28:Dog::PlayDead};29:30:Dog*pDog=0;31:intMethod;32:boolfQuit=false;33:34:while(!fQuit)35:{36:cout<<"(0)Quit(1)Speak(2)Move(3)Eat(4)Growl";37:cout<<"(5)Whimper(6)RollOver(7)PlayDead:";38:cin>>Method;39:if(Method==0)40:{41:fQuit=true;42:}43:else44:{45:pDog=newDog;46:(pDog->*DogFunctions[Method-1])();47:deletepDog;48:}49:}50:return0;51:}Результат:(0)Quit(1)Speak(2)Move(3)Eat(4)Growl(5)Whimper(6)Roll0ver(7)PlayDead:1Woof!(0)Quit(1)Speak(2)Move(3)Eat(4)Growl(5)Whimper(6)Roll0ver(7)PlayDead:4Grrr(0)Quit(1)Speak(2)Move(3)Eat(4)Growl(5)Whimper(6)Roll0ver(7)PlayDead:7IsthistheendofLittleCaesar?(0)Quit(1)Speak(2)Move(3)Eat(4)Growl(5)Whimper(6)Roll0ver(7)PlayDead:0Анализ: В строках 5—15 создается класс Dog, содержащий семь функций-членов,характеризующихсяодинаковымисигнатуройитипомвозврата.Встроке17спомощьюtypedefобъявляетсятипPDFконстантныхуказателейнафункции-членыDog,которыенепринимаютиневозвращаютникакихзначений.В строках 21-28 объявляется массив DogFunctions, предназначенный для храненияуказателейнасемьфункций-членов.В строках 36 и 37 пользователю предлагается выбрать метод.
Выбор любого элемента,кроме Quit, приводит к созданию объекта класса Dog, после чего из массива вызываетсясоответствующий метод (строка 46). Ниже представлена еще одна строка, которая можетнемногосмутитьвашихзнакомыхпрограммистов,работающихсC++:(pDog->*-DogFunctions[Method-1])();Этовыражение,безусловно,немногоэкзотично,носегопомощьюможносоздатьтаблицуфункций-членов,чтосделаеткодпрограммыпрощеичитабельнее.Рекомендуется:Используйтеуказателинафункции-членыдлявызоваметодоввобъектахкласса.Используйтеtypedef,чтобыупроститьобъявлениеуказателянафункцию-член.Нерекомендуется:Незлоупотребляйтесозданиемуказателейнафункции-члены,еслибеэнихможнообойтись.РезюмеСегоднявыпознакомилисьссозданиемстатическихпеременных-членовкласса,которые,вотличие от обычных переменных-членов, принадлежат всему классу, а не отдельному объекту.Еслистатическаяпеременная-членобъявленакакpublic,тообратитьсякнейможнопростопоимени,даженеиспользуяобъектовкласса,которомупринадлежитэтапеременная.Статическиепеременные-членыможноиспользоватьвкачествесчетчиковобъектовкласса.Посколькуонинеявляютсячастямиобъектов,приихобъявлениинепроисходитавтоматическоерезервированиепамяти,какприобъявленииобычныхпеременных-членов.Поэтомувпрограммеза пределами объявления класса обязательно должна быть строка, в которой происходитопределениеиинициализациястатическойпеременной-члена.Статические функции-члены также принадлежат всему классу, подобно статическимпеременным-членам.Вызватьстатическуюфункцию-членклассаможнодажевтомслучае,еслине было создано ни одного объекта этого класса.
Сами же эти функции могут использоватьсядляоткрытиядоступакстатическимпеременным-членам.Посколькустатическиепеременныечленынеимеютуказателяthis,онинемогутиспользоватьобычныепеременные-члены.Из-заотсутствияуказателяthisстатическиефункции-членынемогутобъявлятьсякакconst.Деловтом,чтоприобъявлениифункции-членасоспецификаторомconstустанавливается,чтоуказательthisэтойфункцииявляетсяконстантным.Крометого,выузнали,какобъявлятьииспользоватьуказателинаобычныефункцииинафункции-члены, а также познакомились с созданием массивов этих указателей и с передачейуказателейнафункциивдругиефункции.Как указатели на функции, так и указатели на функции-члены могут использоваться длясоздания таблиц функций, что облегчает управление их вызовом в процессе выполненияпрограммы.Этопридаетпрограммегибкостьиделаетпрограммныйкодболеечитабельным.ВопросыиответыЗачемиспользоватьстатическиеданные,еслиестьглобальные?Область видимости статических данных ограничивается классом.
Обращаться кстатическойпеременной-членуследуетизобъектовкласса,либоизвнешнегокодапрограммы,явноуказавимякласса(вслучае,еслистатическаяпеременная-членописанакакpublic),либоспомощью открытой статической функции-члена этого класса. Статические переменные-членыотносятсяктипуданныхтогокласса,которомуонипринадлежат. Ограничение доступа к членам класса, вызванное строгим контролем затипомданныхвC++,делаетиспользованиестатическихпеременных-членовболеебезопаснымпосравнениюсглобальнымиданными.Зачемиспользоватьстатическиефункции-члены,еслиможновоспользоватьсяглобальнымифункциями?Статические функции-члены принадлежат классу и могут вызываться только с помощьюобъектовклассаилисявнымуказаниемименикласса,например:ClassName::FunctionName().Насколько часто в программах используются указатели на функции и указатели нафункции-члены?Такие указатели используются достаточно редко.
Это дело вкуса программиста. Даже всложныхимощныхпрограммахбезнихможновполнеобойтись.КоллоквиумВэтомразделепредлагаютсявопросыдлясамоконтроляиукрепленияполученныхзнанийи приводится несколько упражнений, которые помогут закрепить ваши практические навыки.Попытайтесьсамостоятельноответитьнавопросытестаивыполнитьзадания,апотомсверьтеполученные результаты с ответами в приложении Г. Не приступайте к изучению материаласледующей главы, если для вас остались неясными хотя бы некоторые из предложенных нижевопросов.Контрольныевопросы1.Могутлистатическиепеременные-членыбытьзакрытыми?2.Объявитестатическуюпеременную-член.3.Объявитестатическуюфункцию.4.
Объявите указатель на функцию, принимающую параметр типа int и возвращающуюзначениетипаlong.5.Изменитеуказатель,созданныйвзадании4,науказательнафункцию-членклассаCar.6.Объявитемассивиздесятиуказателей,созданныхвзадании5.Упражнения1. Напишите короткую программу, объявляющую класс с одной обычной переменнойчленом и одной статической переменной-членом. Создайте конструктор, выполняющийинициализацию переменной-члена и приращение статической переменной-члена. Затемопишитедеструктор,которыйуменьшаетнаединицузначениестатическойпеременной.2. Используя программный блок из упражнения 1, напишите короткую выполняемуюпрограмму, которая создает три объекта, а затем выводит значения их переменных-членов истатическойпеременной-членакласса.Затемпоследовательноудаляйтеобъектыивыводитенаэкранзначениестатическойпеременной-члена.3.
Измените программу упражнения 2 таким образом, чтобы доступ к статическойпеременной-члену осуществлялся с помощью статической функции-члена. Сделайтестатическуюпеременную-члензакрытой.4.Создайтевпрограммеупражнения3указательнафункцию-члендлядоступакзначениюнестатическойпеременной-членаивоспользуйтесьимдлявыводаэтихзначенийнапечать.5. Добавьте две дополнительные переменные-члена к классу из предыдущих заданий.Добавьте методы доступа, возвращающие значения всех этих переменных.
Все функции-членыдолжны возвращать значения одинакового типа и иметь одинаковую сигнатуру. Для доступа кэтимметодамиспользуйтеуказательнафункцию-член.ПодведениеитоговВ этой главе вашему вниманию предлагается достаточно мощная программа, в которойиспользуется большинство средств и подходов программирования, освоенных вами в течениедвухнедель.В этой программе используются связанные списки, виртуальные функции, чистыевиртуальные функции, замещения функций, полиморфизм, открытое наследование, перегрузкафункций, вечные циклы, указатели, ссылки и многие другие знакомые вам средства. Обратитевнимание, что представленный здесь связанный список отличается от рассмотренных ранее.ЯзыкC++предоставляетмножествоспособовдостиженияоднойитойжецели.Цель данной программы состоит в создании функционального связанного списка.
В узлахсозданногоспискаможнохранитьзаписиодеталяхиагрегатах,чтопозволяетиспользоватьегов реальных прикладных программах баз данных складов. Хотя здесь представлена неокончательнаяформапрограммы,онадостаточнохорошодемонстрируетвозможностисозданиясовершенной структуры накопления и обработки данных. Листинг программы содержит 311строк. Попробуйте самостоятельно проанализировать код, прежде чем прочтете анализ,приведенныйпослелистинга.Итогивторойнедели1://**********************************2://3://Название:Подведениеитогов4://5://Файл:Week26://7://Описание:Демонстрациясозданияииспользованиясвязанногосписка8://9://Классы:PART-содержитидентификационный10://номердеталииобеспечиваетвозможность11://добавлятьдругиеданные12://PartNode-функционируеткакузелвPartsList13://14://PartsList-реализуетмеханизмсвязывания15://узловвсписок16://17://**********************************18:19:#include<iostream.h>20:21:22:23://****************Part************24:25://Абстрактныйбазовыйкласс,общийдлявсехдеталей26:classPart27:{28:public:29:Part():itsPartNumber(1){}30:Part(intPartNumber):itsPartNumber(PartNumber){}31:virtual~Part(){};32:intGetPartNumber()const{returnitsPartNumber;}33:virtualvoidDisplay()const=0;//должнабытьзамещенакакprivate34:private:35:intitsPartNumber;36:};37:38://выполнениечистойвиртуальнойфункциив39://стандартномвидедлявсехпроизводныхклассов40:voidPart::Display()const41:{42:cout<<"\nНомердетали:"<<itsPartNumber<<endl;43:}44:45://*************Автомобильныедетали**********46:47:classCarPart:publicPart48:{49:public:50:CarPart():itsModelYear(94){}51:CarPart(intyear,intpartNumber);52:virtualvoidDisplay()const53:{54:Part::Display();cout<<"Годсоздания:";55:cout<<itsModelYear<<endl;56:}57:private:58:intitsModelYear;59:};60:61:CarPart::CarPart(intyear,intpartNumber):62:itsModelYear(year),63:Part(partNumber)64:{}65:66:67://*************Авиационныедетали**********68:69:classAirPlanePart:publicPart70:{71:public:72:AirPlanePart():itsEngineNumber(1){};73:AirPlanePart(intEngineNumber,intPartNumber);74:virtualvoidDisplay()const75:{76:Part::Display();cout<<"Номердвигателя:";77:cout<<itsEngineNumber<<endl;78:}79:private:80:intitsEngineNumber;81:};82:83:AirPlanePart::AirPlanePart(intEngineNumber,intPartNumber):84:itsEngineNumber(EngineNumber),85:Part(PartNumber)86:{}87:88://**************Узлыспискадеталей**********89:classPartNode90:{91:public:92:PartNode(Part*);93:~PartNode();94:voidSetNext(PartNode*node){itsNext=node;}95:PartNode*GetNext()const;96:Part*GetPart()const;97:private:98:Part*itsPart;99:PartNode*itsNext;100:};101:102://ВыполнениеPartNode...103:104:PartNode::PartNode(Part*pPart):105:itsPart(pPart),106:itsNext(0)107:{}108:109:PartNode::~PartNode()110:{111:deleteitsPart;112:itsPart=0;113:deleteitsNext;114:itsNext=0;115:}116:117://ВозвращаетсяNULL,еслинетследующегоузлаPartNode118:PartNode*PartNode::GetNext()const119:{120:returnitsNaxt;121:}122:123:Part*PartNode::GetPart()const124:{125:if(itsPart)126:returnitsPart;127:else128:returnNULL;//ошибка129:}130:131://****************Списокдеталей************132:classPartsList133:{134:public:135:PartsList();136:~PartsList();137: // Необходимо, чтобы конструктор-копировщик и оператор соответствовали другдругу!138:Part*Find(int&position,intPartNumber)const;139:intGetCount()const{returnitsCount;}140:Part*GetFirst()const;141:staticPartsList&GetGlobalPartsList()142:{143:returnGlobalPartsList;144:}145:voidInsert(Part*);146:voidIterate(void(Part::*f)()const)const;147:Part*operator[](int)const;148:private:149:PartNode*pHead;150:intitsCount;151:staticPartsListGlobalPartsList;152:};153:154:PartsListPartsList::GlobalPartsList;155:156://Выполнениесписка...157:158:PartsList::PartsList():159:pHead(0),160:itsCount(0)161:{}162:163:PartsList::^PartsList()164:{165:deletepHead;166:}167:168:Part*PartsList::GetFirst()const169:{170:if(pHead)171:returnpHead->GetPart();172:else173:returnNULL;//ловушкаошибок174:}175:176:Part*PartsList::operator[](intoffSet)const177:{178:PartNode*pNode=pHead;179:180:if(!pHead)181:returnNULL;//ловушкаошибок182:183:if(offSet>itsCount)184:returnNULL;//ошибка185:186:for(inti=0;i<offSet;i++)187:pNode=pNode->GetNext();188:189:returnpNode->GetPart();190:}191:192:Part*PartsList::Find(int&position,intPartNumber)const193:{194:PartNode*pNode=0;195:for(pNode=pHead,position=0;196:pNode!=NULL;197:pNode=pNode->GetNext(),position++)198:{199:if(pNode->GetPart()->GetPartNumber()==PartNumber)200:break;201:}202:if(pNode==NULL)203:returnNULL;204:else205:returnpNode->GetPart();206:}207:208:voidPartsList::Iterate(void(Part::*func)()const)const209:{210:if(!pHead)211:return;212:PartNode*pNode=pHead;213:do214:(pNode->GetPart()->*func)();215:while(pNode=pNode->GetNext());216:}217:218:voidPartsList::Insert(Part*pPart)219:{220:PartNode*pNode=newPartNode(pPart);221:PartNode*pCurrent=pHead;222:PartNode>>pNext=0;223:224:intNew=pPart->GetPartNumber();225:intNext=0;226:itsCount++;227:228:if(!pHead)229:{230:pHead=pNode;231:return;232:}233:234://Еслиэтозначениеменьшеголовногоузла,235://тотекущийузелстановитсяголовным236:if(pHead->GetPart()->GetPartNumber()->New)237:{238:pNode->SetNext(pHead);239:pHead=pHode;240:return;241:}242:243:for(;;)244:{245://Еслинетследующего,вставляетсятекущий246:if(!pCurrent->GetNext())247:{248:pCurrent->SetNext(pNode);249:return;250:}251:252://Еслитекущийбольшепредыдущего,номеньшеследующего,товставляем253://здесь.ИначеприсваиваемзначениеуказателяNext254:pNext=pCurrent->GetNext();255:Next=pNext->GetPart()->GetPartNumber();256:if(Next>New)257:{258:pCurrent->SetNext(pNode);259:pNode->SetNext(pNext);260:return;261:}262:pCurrent=pNext;263:}264:}265:266:intmain()267:{268:PartsList&pl=PartsList::GetGlobalPartsList();269:Part*pPart=0;270:intPartNumber;271:intvalue;272:intChoice;273:274:while(1)275:{276:cout<<"(0)Quit(1)Car(2)Plane:";277:cin>>choice;278:279:if(!choice)280:break;281:282:cout<<"NewPartNumber?:";283:cin>>PartNumber;284:285:if(choice==1)286:{287:cout<<"ModelYear?:";288:cin>>value;289:pPart=newCarPart(value,PartNumber);290:}291:else292:{293:cout<<"EngineNumber?:";294:cin>>value;295:pPart=newAirPlanePart(value,PartNumber);296:}297:298:pl.Insert(pPart);299:}300:void(Part::*pFunc)()const=Part::Display;301:pl.Iterate(pFunc);302:return0;303:}Результат:(0)Quit(1)Car(2)Plane:1NewPartNumber?:2837ModelYear?90(0)Quit(1)Car(2)Plane:2NewPartNumber?:378EngineNumber?:4938(0)Quit(1)Car(2)Plane:1NewPartNumber?:4499ModelYear?:94(0)Quit(1)Car(2)Plane:1NewPartNumber?:3000ModelYear?93(0)Quit(1)Car(2)Plane:0PartNumber:378EngineNo.:4938PartNumber:2837ModelYear:90PartNumber:3000ModelYear:93PartNumber:4499ModelYear:94Представленная программа создает связанный список объектов класса Part.