246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 69
Текст из файла (страница 69)
Не лишним будет еще разнапомнить, что эта переменная-член относится не к какому-либо определенному объекту, а ковсемуклассувцелом.Поэтомуеслионаобъявленакакpublic,томожетиспользоватьсялюбойфункциейпрограммы.Если статическая переменная-член будет объявлена как закрытая, то доступ к ней можнополучитьспомощьюфункции-члена.Нодляэтогонеобходимоналичиехотябыодногообъектаданного класса. Именно такой подход реализован в листинге 14.3.
Затем мы перейдем кизучениюстатическихфункций-членов.Листинг14.3.Доступкстатическимчленамспомощьюобычныхфункций-членов1://Листинг14.3.Закрытыестатическиепеременные-члены2:3:#include<iostream.h>4:5:classCat6:{7:public:8:Cat(intage):itsAge(age){HowManyCats++;}9:virtual~Cat(){HowManyCats--;}10:virtualintGetAge(){returnitsAge;}11:virtualvoidSetAge(intage){itsAge=age;}12:virtualintGetHowMany(){returnHowManyCats;}13:14:15:private:16:intitsAge;17:staticintHowManyCats;18:};19:20:intCat::HowManyCats=0;21:22:intmain()23:{24:constintMaxCats=5;inti;25:Cat*CatHouse[MaxCats];26:for(i=0;i<MaxCats;i++)27:CatHouse[i]=newCat(i);28:29:for(i=0;i<MaxCats;i++)30:{31:cout<<"Thereare";32:cout<<CatHouse[i]->GetHowMany();33:cout<<"catsleft!\n";34:cout<<"Deletingtheonewhichis";35:cout<<CatHouse[i]->GetAge()+2;36:cout<<"yearsold\n";37:deleteCatHouse[i];38:CatHouse[i]=0;39:}40:return0;41:}Результат:Thereare5catsleft!Deletingtheonewhichis2yearsoldThereare4catsleft!Deletingtheonewhichis3yearsoldThereare3catsleft!Deletingtheonewhichis4yearsoldThereare2catsleft!Deletingtheonewhichis5yearsoldThereare1catsleft!Deletingtheonewhichis6yearsoldАнализ: В строке 17 статическая переменная-член HowManyCats объявлена как private.Поэтомутеперьдоступкнейзакрытдляфункций,неявляющихсячленамикласса,напримердляфункцииTelepathicFunctionизпредыдущеголистинга.Хотя переменная HowManyCats является статической, она все же находится в областивидимости класса.
Поэтому любая функция класса, например GetHoqMany(), может получитьдоступкнейтакже,какклюбойобычнойпеременной-члену.ОднакодлявызоваGetHowMany()функциядолжнаиметьобъект,черезкоторыйосуществляетсявызов.Рекомендуется:Применяйте статические переменные-члены для совместногоиспользования данных несколькими объектами класса. Ограничьте доступ к статическимпеременным-членам,объявивихкакprivateилиprotected.Нерекомендуется:Неиспользуйтестатическиеперемен-ные-членыдляхраненияданныходногообъекта.Этипеременныепредназначеныдляобменаданнымимеждуобъектами.Статическиефункции-членыСтатические функции-члены подобны статическим переменным-членам: они непринадлежатодномуобъекту,анаходятсявобластивидимостивсегокласса.Именнопоэтомуихможно вызывать даже в тех случаях, когда не было создано ни одного объекта класса, какпоказановлистинге14.4.Листинг14.4.Статическиефункции-члены1://Листинг14.4.Статическиефункции-члены2:3:#include<iostream.h>4:5:classCat6:{7:public:8:Cat(intage):itsAge(age){HowManyCats++;}9:virtual~Cat(){HowManyCats--;}10:virtualintGetAge(){returnitsAge;}11:virtualvoidSetAge(intage){itsAge=age;}12:staticintGetHowMany(){returnHowManyCats;}13:private:14:intitsAge;15:staticintHowManyCats;16:};17:18:intCat::HowManyCats=0;19:20:voidTelepathicFunction();21:22:intmain()23:{24:constintMaxCats=5;25:Cat*CatHouse[MaxCats];inti;26:for(i=0;i<MaxCats;i++)27:{28:CatHouse[i]=newCat(i);29:TelepathicFunction();30:}31:32:for(i=0;i<MaxCats;i++),33:{34:deleteCatHouse[i];35:TelepathicFunction();36:}37:return0;38:}39:40:voidTelepathicFunction()41:{42:cout<<"Thereare"<<Cat::GetHowMany()<<"catsalive!\n";43:}Результат:Thereare1catsalive!Thereare2catsalive!Thereare3catsalive!Thereare4catsalive!Thereare5catsalive!Thereare4catsalive!Thereare3catsalive!Thereare2catsalive!Thereare1catsalive!Thereare0catsalive!Анализ:Встроке15вобъявленииклассаCatсоздаетсязакрытаястатическаяпеременнаячленHowManyCats.Встроке12объявляетсяоткрытаястатическаяфункция-членGetHowMany().Так как функция GetHowMany() открыта, доступ к ней может получить любая другаяфункция, а при объявлении ее статической отпадает необходимость в существовании объектатипа Cat.
Именно поэтому функция TelepathicFunction() в строке 42 может получить доступ кGetHowMany(), не имея доступа к объекту Cat. Конечно же, к функции GetHowMany() можнобылообратитьсяизблокаmain()также,каккобычнымметодамобъектовCat.Примечание: Статические функции-члены не содержат указателя this. Поэтому они немогут объявляться со спецификатором const. Кроме того, поскольку функции-члены получаютдоступ к переменным-членам с помощью указателя this, статические функции-члены не могутиспользоватьобычныенестатическиепеременные-члены!Статическиефункции-членыДоступ к статическим функциям-членам можно получить, либо вызывая их из объектовкласса как обычные функции-члены, либо вызывая их без объектов, явно указав в этом случаеимякласса.Пример:classCat{public:staticintGetHowMany(){returnHowManyCats;}private:staticintHowManyCats;}intCat::HowManyCats=0;intmain(){inthowMany;CattheCat;//определениеобьектаhowMany=theCat.GetHowMany();//доступчерезобъектhowMany=Cat::GetHowMany();//доступбезобъекта}УказателинафункцииТочно так же, как имя массива постоянно указывает на его первый элемент, имя функцииявляется указателем на саму функцию.
Можно объявить переменную-указатель функции и вдальнейшемвызыватьееспомощьюэтогоуказателя.Такаявозможностьможетоказатьсявесьмаполезной, поскольку позволяет создавать программы, в которых функции вызываются покомандампользователя,вводимымсклавиатуры.Единственная важная деталь для определения указателя на функцию — знание типаобъекта,накоторыйссылаетсяуказатель.Указательтипаintобязательносвязансцелочисленнойпеременной. Аналогичным образом указатель на функцию может вызывать только функции сзаданнымисигнатуройитипомвозврата.Вобъявленииlong(*funoPtr)(int);создается указатель на функцию funcPtr (обратите внимание на символ * перед именемуказателя), которая принимает целочисленный параметр и возвращает значение типа long.Круглые скобки вокруг (*funcPtr) обязательны, поскольку скобки вокруг (int) имеют большийприоритетпосравнениюсоператоромкосвенногообращения(*).Еслиубратьпервыескобки,тоэто выражение будет объявлять функцию funcPtr, принимающую целочисленный параметр ивозвращающую указатель на значение типа long.
(Вспомните, что все пробелы в C++игнорируются,)Рассмотримдваследующихобъявления:long*Function(int);long(*funcPtr)(int);В первой строке Function() — это функция, принимающая целочисленный параметр ивозвращающаяуказательнапеременнуютипаlong.ВовторомпримереfuncPtr—этоуказательнафункцию,принимающуюцелочисленныйпараметривозвращающуюпеременнуютипаlong.Объявление указателя на функцию всегда содержит тип возвращаемой переменной изаключенный в скобки список типов формальных параметров, если таковые имеются. Примеробъявленияииспользованияуказателянафункциюпоказанвлистинге14.5.Листинг14.5.Указателинафункцию1://Листинг14.5.Использованиеуказателейнафункции2:3:#include<iostream.h>4:5:voidSquare(int&,int&);6:voidCube(int&,int&);7:voidSwap(int&,int&);8:voidGetVals(int&,int&);9:voidPrintVals(int,int);10:11:intmain()12:{13:void(*pFunc)(int&,int&);14:boolfQuit=false;15:16:intval0ne=1,valTwo=2;17:intchoice;18:while(fQuit==false)19:{20:cout<<"(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap";21:cin>>choice;22:switch(choice)23:{24:case1:pFunc=GetVals;break;25:case2:pFunc=Square;break;26:case3:pFunc=Cube;break;27:case4:pFunc=Swap;break;28:default:fQuit=true;break;29:}30:31:if(fQuit)32:break;33:34:PrintVals(valOne,valTwo);35:pFunc(valOne,valTwo);36:PrintVals(valOne,valTwo);37:}38:return0;39:}40:41:voidPrintVals(intx,inty)42:{43:cout<<"x:"<<x<<"y:"<<y<<endl;44:}45:46:voidSquare(int&rX,int&rY)47:{48:rX*=rX;49:rY*=rY;50:}51:52:voidCube(int&rX,int&rY)53:{54:inttmp;55:56:tmp=rX;57:rX*=rX;58:rX=rX*tmp;59:60:tmp=rY;61:rY*=rY;62:rY=rY*tmp;63:}64:65:voidSwap(int&rX,int&rY)66:{67:inttemp;68:temp=rX;69:rX=rY;70:rY=temp;71:}72:73:voidGetVals(int&rValOne,int&rValTwo)74:{75:cout<<"NewvalueforValOne:";76:cin>>rValOne;77:cout<<"NewvalueforValTwo:";78:cin>>rValTwo;79:}Результат:(0)0uit(1)ChangeValues(2)Square(3)Cube(4)Swap:1x:1у:2NewvalueforValOne:2NewvalueforValTwo:3x:2y:3(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:3x:2y:3x:8y:27(0)Qult(1)ChangeValues(2)Square(3)Cube(4)Swap:2x:8y:27x:64y:729(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:4x:64y:729x:729y:64(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:0Анализ: В строках 5—8 объявляются четыре функции с одинаковыми типами возврата исигнатурами.Всеэтифункциивозвращаютvoidипринимаютссылкиназначениятипаint.В строке 13 переменная pFunc объявлена как указатель на функцию, принимающую двессылки на int и возвращающую void.
Этот указатель может ссылаться на каждую изупоминавшихся ранее функций. Пользователю предлагается выбрать функцию, после чего онасвязывается с указателем pFunc. В строках 34—36 выводятся текущие значения двухцелочисленныхпеременных,вызываетсятекущаяфункцияивыводятсярезультатывычислений.УказателинафункцииОбращениекфункциичерезуказательзаписываетсятакже,какиобычныйвызовфункции,на которую он указывает.
Просто вместо имени функции используется имя указателя на этуфункцию.Чтобы связать указатель на функцию с определенной функцией, нужно просто присвоитьемуимяфункциибезкаких-либоскобок.Имяфункции.каквыужезнаете,представляетсобойконстантныйуказательнасамуфункцию.Поэтомууказательнафункциюиспользуетсятакже,какиееимя.Привызовефункциичерезуказательследуетзадатьвсепараметры.установленныедлятекущейфункции.Пример:long(*pFuncOne)(int,int);longSomeFunction(int,int);pFuncOne=SomeFunction;pFuncOne(5,7);ЗачемнужныуказателинафункцииВ программе, показанной в листинге 14.5, можно было бы обойтись и без указателей нафункции, однако с их помощью значительно упрощается и становится читабельнее кодпрограммы:достаточнотольковыбратьфункциюизспискаизатемвызватьее.В листинге 14.6 используются прототипы и объявления функций листинга 14.5, ноотсутствуютуказателинафункции.Оценитеразличиямеждуэтимидвумялистингами.Листинг14.6.Видоизмененныйвариантлистинга14.5безиспользованияуказателейнафункции1://Листинг14.6.Видоизмененныйвариантлистинга14.5безиспользования2://указателейнафункции3:#include<iostream.h>4:5:voidSquare(int&,int&);6:voidCube(int&,int&);7:voidSwap(int&,int&);8:voidGetVals(int&,int&);9:voidPrintVals(int,int);10:11:intmain()12:{13:boolfQuit=false;14:intvalOne=1,valTwo=2;15:intchoice;16:while(fQuit==false)17:{18:cout<<<<"(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap";19:cin>>choice;20:switch(choice)21:{22:case1:23:PrintVals(valOne,valTwo);24:GetVals(valOne,valTwo);25:PrintVals(valOne,valTwo);26:break;27:28:case2:29:PrintVals(valOne,valTwo);20:Square(valOne,valTwo);31:PrintVals(valOne,valTwo);32:break;33:34:case3:35:PrintVals(valOne,valTwo);36:Cube(valOne,valTwo);37:PrintVals(valOne,valTwo);38:break;39:40:case4:41:PrintVals(valOne,valTwo);42:Swap(valOne,valTwo);43:PrintVals(valOne,valTwo);44:break;45:46:default:47:fOuit=true;48:break;49:}50:51:if(fQuit)52:break;53:}54:return0;55:}56:57:voidPrintVals(intx,inty)58:{59:cout<<"x:"<<x<<"y:"<<y<<endl;60:}61:62:voidSquare(int&rX,int&rY)63:{64:rX*=rX;65:rY*=rY;66:}67:68:voidCube(int&rX,int&rY)69:{70:inttmp;71:72:tmp=rX;73:rX*=rX;74:rX=rX*tmp;75:76:tmp=rY;77:rY*=rY;78:rY=rY*tmp;79:}80:81:voidSwap(int&rX,int&rY)82:{83:inttemp;84:temp=rX;85:rX=rY;86:rY=temp;87:}88:89:voidGetVals(int&rValOne,int&rValTwo)90:{91:cout<<"NewvalueforValOne:";92:cin>>rValOne;93:cout<<"NewvalueforValTwo:";94:cin>>rValTwo;95:}Результат:(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:1х.1у.2NewvalueforValOne:2NewvalueforValTwo:3(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:3x:2y:3x:8y:27(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:2x:8y:27x:64y:729(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:4x:64y:729x:729y:64(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:0Анализ: Функции работают так же, как и в листинге 14.5.