246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 70
Текст из файла (страница 70)
Информация, выводимаяпрограммойнаэкран,такженеизменилась.Норазмерпрограммыувеличилсяс22до46строк.Причина в том, что вызов функции PrintVals() приходится повторять для каждого блока соператоромcase.Заманчивым может показаться вариант размещения функции PrintVals() вверху и внизуцикла while, а не в каждом блоке с оператором case.
Но тогда функция PrintVals() будетвызыватьсядажевслучаевыходаизцикла,чегобытьнедолжно.УпрощенныйвариантвызовафункцииИмя указателя на функцию вовсе не должно дублировать имя самой функции, хотя вывполне вправе это сделать. Пусть, например, pFunc—указатель на функцию, принимающуюцелочисленноезначениеивозвращающуюпеременнуютипа1опд,иимяэтойфункции—pFunc.Увасестьвозможностьвызватьеелюбымиздвухобращений:pFunc(x);или(*pFunc)(x);Обавыраженияприведуткодномуитомужерезультату.Хотяпервоевыражениеко-роче,второепридаетпрограммебольшегибкости.Увеличениеразмеракодазасчетповторяющихсявызововоднойитойжефункцииухудшаетчитабельность программы.
Этот вариант приведен специально для того, чтобы показаэффективность использования указателей на функции. В реальных условиях преимуществаприменения указателей на функции еще более очевидны, так как они позволяют исключитьдублированиекодаиделаютпрограммуболеечеткой.Например,указателинафункцииможнособратьводинмассививызыватьизнегофункциивзависимостиоттекущейситуации.МассивыуказателейнафункцииАналогично объявлению массива указателей целых чисел можно объявить массивуказателей на функции с определенной сигнатурой, возвращающих значения определенноготипа.
Листинг 14.7 является еще одним вариантом программы из листинга 14.5, в которой всеуказателинафункциисобранывмассив.Листинг14.7.Использованиемассивауказателейнафункции1://Листинг14.7.Примериспользованиямассивауказателейнафункции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:intvalOne=1,valTwo=2;14:intchoice,i;15:constMaxArray=5;16:void(*pFuncArray[MaxArray])(int&,int&);17:18:for(i=0;i<MaxArray;i++)19:{20:cout<<"(1)ChangeValues(2)Square(3)Cube(4)Swap:";21:cin>>choice;22:switch(choice)23:{24:case1:pFuncArray[i]=GetVals;break;25:case2:pFuncArray[i]=Square;break;26:case3:pFuncArray[i]=Cube;break;27:case4:pFuncArray[i]=Swap;break;28:default:pFuncArray[i]=0;29:}30:}31:32:for(i=0;i<MaxArray;i++)33:{34:if(pFuncArray[i]==0)35:continue;36:pFuncArray[i](valOne,valTwo);37:PrintVals(valOne,valTwo);38:}39:return0;40:}41:42:voidPrintVals(intx,intу)43:{44:cout<<"x:"<<x<<"у:"<<у<<endl;45:}46:47:voidSquare(int&rX,int&rY)48:{49:rX*=rX;50:rY*=rY;51:}52:53:voidCube(int&rX,int&rY)54:{55:inttmp;56:57:tmp=rX;58:rX*=rX;59:rX=rX*tmp;60:61:tmp=rY;62:rY*=rY;63:rY=rY*tmp;64:}65:66:voidSwap(int&rX,int&rY)67:{68:inttemp;69:temp=rX;70:rX=rY;71:rY=temp;72:}73:74:voidGetVals(int&rValOne,int&rValTwo)75:{76:cout<<"NewvalueforValOne:";77:cin>>rValOne;78:cout<<"NewvalueforValTwo:";79:cin>>rValTwo;80:}Результат:(1)ChangeValues(2)Square(3)Cube(4)Swap:1(1)ChangeValues(2)Square(3)Cube(4)Swap:2(1)ChangeValues(2)Square(3)Cube(4)Swap:3(1)ChangeValues(2)Square(3)Cube(4)Swap:4(1)ChangeValues(2)Square(3)Cube(4)Swap:2NewValueforValOne:2NewValueforValTwo:3x:2y:3x:4y:9x:64y:729x:729y:64x:531441y:4096Анализ:Какивпредыдущемлистинге,дляэкономииместанебылипоказанывыполненияобъявленных функций, поскольку сами функции остались теми же, что и в листинге 14.5.
Встроке 16 объявляется массив pFuncArray, содержащий пять указателей на функции, которыевозвращаютvoidипринимаютдвессылкиназначениятипаint.В строках 18-30 пользователю предлагается установить последовательность вызовафункций. Каждый член массива связывается с соответствующей функцией. Последовательныйвызов функции осуществляется в строках 32-38, причем после каждого вызова на экран сразувыводитсярезультат.ПередачауказателейнафункциивдругиефункцииУказателинафункции(илимассивыуказателей)могутпередаватьсявдругиефункциидлявызовавнихспомощьюуказателянужнойфункции.Листинг14.5можноусовершенствовать,передавуказательнавыбраннуюфункциюдругойфункции (кроме main()), которая выведет исходные значения на печать, вызовет функцию ивновьнапечатаетизмененныезначения.Именнотакойподходпримененвлистинге14.8.Листинг14.8.Переданауказателянафункциюдругойфункции1://Листинг14.8.Передачауказателянафункциюдругойфункции2:3:#include<iostream.h>4:5:voidSquare(int&,int&);6:voidCube(int&,int&);7:voidSwap(int&,int&);8:voidGetVals(int&,int&);9:voidPrintVals(void(*)(int&,int&),int&,int&);10:11:intmain()12:{13:intval0ne=1,valTwo=2;14:intchoice;15:boolfQuit=false;16:17:void(*pFunc)(int&,int&);18:19:while(fQuit==false)20:{21:cout<<"(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:";22:cin>>choice;23:switch(choice)24:{25:case1:pFunc=GetVals;break;26:case2:pFunc=Square;break;27:case3:pFunc=Cube;break;28:case4:pFunc=Swap;break;29:default:fQuit=true;break;30:}31:if(fQuit==true)32:break;33:PrintVals(pFunc,valOne,valTwo);34:}35:36:return0;37:}38:39:voidPrintVals(void(*pFunc)(int&,int&),int&x,int&у)40:{41:cout<<"x:"<<x<<"у:"<<у<<endl;42:pFunc(x,у);43:cout<<"x:"<<x<<"у:"<<у<<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)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:1x:1у:2NewvalueforVal0ne:2NewvalueforValTwo:3x:2у:3(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:3x:2у:3x:8у:27(O)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:2x:8у:27x:64у:729(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:4x:64у:729x:729y:64(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:0Анализ:Встроке17объявляетсяуказательнафункциюpFunc,принимающуюдвессылкинаintивозвращающуюvoid.ФункцияPrintVals,длякоторойзадаетсятрипараметра,объявляетсяв строке 9.
Первым в списке параметров стоит указатель на функцию, возвращающую void ипринимающую две ссылки на int. Второй и третий параметры функции PrintVals представляютсобой ссылки на значения типа int. После того как пользователь выберет нужную функцию, встроке33происходитвызовфункцииPrintVals.Спросите у знакомого программиста, работающего с C++, что означает следующеевыражение:voidPrintVals(void(*)(int&,int&),int&,int&);Это вид объявлений, который используется крайне редко и заставляет программистовобращаться к книгам каждый раз, когда нечто подобное встречается в тексте. Но временамиданныйподходпозволяетзначительноусовершенствоватькодпрограммы,каквнашемпримере.ИспользованиеtypedefсуказателяминафункцииКонструкция void (*)(int&, int&) весьма громоздка. Для ее упрощения можновоспользоватьсяключевымсловомtypedef,объявивновыйтип(назовемегоVPF)указателейнафункции, возвращающие void и принимающие две ссылки на значения типа int.
Листинг 14.9представляетсобойпереписаннуюверсиюлистинга14.8сиспользованиемэтогоподхода.Листинг 14.8. Использование оператора typedef для объявления типа указателей нафункции1://Листинг14.9.Использованиеtypedefдля2://объявлениятипауказателейнафункции3:#include<iostream.h>4:5:voidSquare(int&,int&);6:voidCube(int&,int&);7:voidSwap(int&,int&);8:voidGetVals(int&,int&);9:typedefvoid(*VPF)(int&,int&);10:voidPrintVals(VPF,int&,int&);11:12:intmain()13:{14:intval0ne=1,valTwo=2;15:intchoice;16:boolfQuit=false;17:18:VPFpFunc;19:20:while(fQuit==false)21:{22:cout<<"(0)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:";23:cin>>choice;24:switch(choice)25:{26:case1:pFunc=GetVals;break;27:case2:pFunc=Square;break;28:case3:pFunc=Cube;break;29:case4:pFunc=Swap;break;30:default:fQuit=true;break;31:}32:if(fQuit==true)33:break;34:PrintVals(pFunc,valOne,valTwo);35:}36:return0;37:}38:39:voidPrintVals(VPFpFunc,int&x,int&y)40:{41:cout<<"x:"<<x<<"y:"<<y<<endl;42:pFunc(x,y);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)Quit(1)ChangeValues(2)Square(3)Cube(4)Swap:1x:1y:2NewvalueforValOne:2NewvalueforValTwo:3x:2y: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Анализ:Встроке9спомощьюоператораtypedefобъявляетсяновыйтипVPFкакуказателина функции, возвращающие void и принимающие две ссылки на int.
В строке 10 объявляетсяфункцияPrintVals(),котораяпринимаеттрипараметра:VPFидвессылкинаinteger.Встроке18указательPfuncобъявляетсякакпринадлежащийTnnyVPF.После объявления типа VPF дальнейшее использование указателя pFunc и функцииPrintVals() становится проще и понятнее. Информация, выводимая программой на экран, неизменилась.УказателинафункциичленыДо настоящего времени все создаваемые указатели на функции использовались для общихфункций, не принадлежащих к какому-нибудь одному классу.
Однако разрешается создаватьуказателиинафункции,являющиесячленамиклассов(методы).Для создания такого указателя используется тот же синтаксис, что и для указателя наобычнуюфункцию,носдобавлениемимениклассаиоператораобластивидимости(::).Такимобразом, объявление указателя pFunc на функции-члены класса Shape, принимающие двацелочисленныхпараметраивозвращающиеvoid,выглядитследующимобразом:void(Shape::*pFunc)(int,int);Указатели на функции-члены используются так же, как и рассмотренные ранее указателипростых функции.
Единственное отличие состоит в том, что для вызова функции необходимоналичиеобъектасоответствующегокласса,длякотороговызываютсяфункции.Влистинге14.10показаноиспользованиеуказателянаметодкласса.Листинг14.10.Указателинафункции-члены1://Листинг14.10.Указателинавиртуальныефункции-члены2:3:#include<iostream.h>4:5:classMammal6:{7:public:8:Mammal():itsAge(1){}9:virtual~Mammal(){}10:virtualvoidSpeak()const=0;11:virtualvoidMove()const=0;12:protected:13:intitsAge;14:};15:16:classDog:publicMammal17:{18:public:19:voidSpeak()const{cout<<"Woof!\n";}20:voidMove()const{cout<<"Walkingtoheel...\n";}21:};22:23:24:classCat:publicMammal25:{26:public:27:voidSpeak()const{cout<<"Meow!\n";}28:voidMove()const{cout<<"slinking...\n";}29:};30:31:32:classHorse:publicMammal33:{34:public:35:voidSpeak()const{cout<<"Whinny!\n";}36:voidMove()const1cout<<"Galloping...\n";}37:};38:39:40:intmain()41:{42:void(Mammal::*pFunc)()const=0;43:Mammal*ptr=0;44:intAnimal;45:intMethod;46:boolfQuit=false;47:48:while(fQuit==false)49:{50:cout<<"(0)Quit(1)dog(2)cat(3)horse51:cin>>Animal;52:switch(Animal)53:{54:case1:ptr=newDog;break;55:case2:ptr=newCat;break;56:case3:ptr=newHorse;break;57:default:fQuit=true;break;58:}59:if(fQuit)60:break;61:62:cout<<"(1)Speak(2)Move:";63:cin>>Method;64:switch(Method)65:{66:case1:pFunc=Mammal::Speak;break;67:default:pFunc=Mammal::Move;break;68:}69:70:(ptr->*pFunc)();71:deleteptr;72:}73:return0;74:}Результат:(0)Quit(1)dog(2)cat(3)horse:1(1)Speak(2)Move:1Woof!(0)Quit(1)dog(2)cat(3)horse:2(1)Speak(2)Move:1Meow!(0)Quit(1)dog(2)cat(3)horse:3(1)Speak(2)Move:2Galloping(0)Quit(1)dog(2)cat(3)horse:0Анализ: В строках 4—14 объявляется тип абстрактных данных Mammal с двумя чистымивиртуальнымиметодамиSpeak()иMove().ОтклассаMammalпроизводятсяподклассыDog,CatиHorse,вкаждомизкоторыхзамещаютсясоответствующимобразомфункцииSpeak()иMove().Впроцессевыполнениятелафункцииmain()пользователюпредлагаетсявыбратьживотное,послечеговобластидинамическогообменасоздаетсяновыйподклассвыбранногоживотного,адрескоторогоприсваиваетсявстроках54—56указателюptr.Затем пользователь выбирает метод, который связывается с указателем pFunc.