246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 41
Текст из файла (страница 41)
Во-первых, необходимость неоднократно разыменовывать указателивнутри функции swap() создает благоприятную почву для возникновения ошибок, кроме того,операции разыменовывания трудно читаются. Во-вторых, необходимость передавать адресапеременных из вызывающей функции нарушает принцип инкапсуляции выполнения функцииswap().Суть программирования на языке C++ состоит в сокрытии от пользователей функциидеталей ее выполнения.
Передача параметров с помощью указателей перекладываетответственность за получение адресов переменных на вызывающую функцию, вместо тогочтобы сделать это в теле вызываемой функции. Другое решение той же задачи предлагается влистинге9.7,вкоторомпоказанаработафункцииswap()сиспользованиемссылок.Листинг9.7.Тажефцнкцияswap(),носиспользованиемссылок1://Листинг9.7.Примерпередачиаргументовкак2://ссылокспомощьюссылок!3:4:#include<iostream.h>5:6:voidswap(int&x,int&y);7:8:intmain()9:{10:intx=5,у=10;11:12:cout<<"Main.Beforeswap,x:"<<x<<"у:"<<у<<"\n";13:swap(x,у);14:cout<<"Main.Afterswap,x:"<<x<<"у:"<<у<<"\n";15:return0;16:}17:18:voidswap(int&rx,int&ry)19:{20:inttemp;21:22:cout<<"Swap.Beforeswap,rx:"<<rx<<"ry:"<<ry<<"\n";23:24:temp=rx;25:rx=ry;26:ry=temp;27:28:cout<<"Swap.Afterswap,rx:"<<rx<<"ry:"<<ry<<"\n";29:30:}Результат:Main.Beforeswap,x:5y:10Swap.Beforeswap,rx:5ry:10Swap.Afterswap,rx:10ry:5Main.Afterswap,x:10,y:5Анализ: Точно так же, как и в примере с указателями, в строке 10 объявляются двепеременные, а их значения выводятся на экран в строке 12.
В строке 13 вызывается функцияswap(),нообратитевниманиенато,чтоейпередаютсяименнозначенияxиу,анеихадреса.Вызывающаяфункцияпростопередаетсвоипеременные.Послевызовафункцииswap()выполнениепрограммыпереходиткстроке18,вкоторойэтипеременные идентифицируются как ссылки. Их значения выводятся на экран в строке 22, нозаметьте, что для этого не требуется никаких специальных операторов, поскольку мы имеемделоспсевдонимамидляисходныхзначенийииспользуемихвэтомкачестве.Встроках24—26выполняетсяобмензначений,послечегоонивыводятсянаэкранвстроке28.
Управление программой вновь возвращается в вызывающую функцию, и в строке 14 этизначенияопятьвыводятсянаэкран,ноужевфункцииmain().Посколькупараметрыдляфункцииswap() объявлены как ссылки, то и переменные из функции main() передаются как ссылки,следовательно,онитакжеизменяютсяивфункцииmain().Такимобразом,благодаряиспользованиюссылокфункцияприобретаетновуювозможностьизменятьисходныеданныеввызывающейфункции,хотяприэтомсамвызовфункцииничемнеотличаетсяотобычного!ПредставленияозаголовкахфункцийипрототипахВлистинге9.6показанафункцияswap(),использующаявкачествеаргументовуказатели,авлистинге9.7—тажефункция,носиспользованиемссылок.Использоватьфункцию,котораяпринимает в качестве параметров ссылки, легче, да и в программе такая функция прощечитается,нокаквызывающейфункцииузнатьотом,какимспособомпереданыпараметры—поссылкеилипозначению?Будучиклиентом(илипользователем)функцииswap(),программистдолженбытьуверенвтом,чтофункцияswap()насамомделеизменитпараметры.Самое время вспомнить о прототипе функции, для которого в данном контексте нашлосьеще одно применение.
Изучив параметры, объявленные в прототипе, который обычнорасполагается в файле заголовка вместе со всеми другими прототипами, программист будетточно знать, что значения, принимаемые функцией swap(), передаются как ссылки,следовательно,обмензначенийпроизойдетдолжнымобразом.Еслибыфункцияswap()былафункцией-членомкласса,тообъявлениеэтогокласса,такжерасположенноевфайлезаголовка,обязательносодержалобыэтуинформацию.В языке C++ клиенты классов и функций всю необходимую информацию черпают изфайлов заголовков. Этот файл выполняет роль интерфейса с классом или функцией,действительная реализация которых скрыта от клиента.
Это позволяет программистусосредоточиться на собственных проблемах и использовать класс или функцию, не вникая вдеталиихработы.КогдаКолонелДжонРоблинг(ColonelJohnRoebling)проектировалсвойБруклинскиймост(Brooklyn Bridge), он интересовался деталями процесса литья и изготовления проводов. Онглубоко вникал в подробности механических и химических процессов, которые требовалосьобеспечитьдлясозданиянеобходимыхматериалов.Новнашидниинженерыболееэффективноиспользуют свое рабочее время, доверяя информации о строительных материалах и неинтересуясьподробностямиихизготовления.В этом и состоит цель языка C++ — позволить программистам полагаться на описанныеклассы и функции, не вникая во внутренние механизмы их действия.
Эти составные частиможно собрать в одну программу, подобно тому как строители из отдельных блоков, проводов,труб,кирпичейидругихэлементовсоздаютдомаимосты.Подобно инженеру, изучающему технические характеристики трубы, чтобы узнать еепропускную способность, объем, размеры арматуры и пр., программист читает интерфейсыфункций и классов, чтобы определить, какой сервис предоставляет данный компонент, какиепараметрыонпринимаетикакиезначениявозвращает.ВозвращениенесколькихзначенийКак упоминалось выше, функции могут возвращать только одно значение. Что же делать,если нужно получить от функции сразу два значения? Один путь решения этой проблемы —передача функции двух объектов как ссылок.
В ходе выполнения функция присвоит этимобъектам нужные значения. Факт передачи объектов как ссылок, позволяющий функцииизменитьисходныеобъекты,равносиленразрешениюданнойфункциивозвратитьдвазначения.В этом случае мы обходимся без возвращаемого значения, которое (зачем же пропадать добру)можноиспользоватьдлясообщенияобошибках.И вновь одинакового результата можно достичь, используя как ссылки, так и указатели. Влистинге 9.8 показана функция, которая возвращает три значения: два в виде параметровуказателейиодноввидевозвращаемогозначенияфункции.Листинг9.8.Возвращениезначенийспомощьюуказателей1://Листинг9.8.2://Возвращениенесколькихзначенийизфункцииспомощьюуказателей3:4:#include<iostream.h>5:int6:shortFactor(intn,int*pSquared,int*pCubed);7:8:intmain()9:{10:intnumber,squared,cubed;11:shorterror;12:13:cout<<"Enteranumber(0-20):";14:cin>>number;15:16:error=Factor(number,&squared,&cubed);17:18:if(!error)19:{20:cout<<"number:"<<number<<"\n";21:cout<<"square:"<<squared<<"\n";22:cout<<"cubed:"<<cubed<<"\n";23:}24:else25:cout<<"Errorencountered!!\n";26:return0;27:}28:29:shortFactor(intn,int*pSquared,int*pCubed)30:{31:shortValue=0;32:if(n>20)33:Value=1;34:else35:{36:*pSquared=n*n;37:*pCubed=n*ri*n;38:Value=0;39:}40:returnValue;41:}Результат:Enteranumber(0-20):3number:3square:9cubed:27Анализ:Встроке10переменныеnumber,squaredиcubedопределяютсясиспользованиемтипаint.Переменнойnumberприсваиваетсязначение,введенноепользователем.Этозначение,атакжеадресапеременныхsquaredиcubedпередаютсяфункцииFactor()ввидепараметров.ВфункцииFactor()анализируетсяпервыйпараметр,которыйпередаетсякакзначение.Еслион больше 20 (максимальное значение, которое может обработать эта функция), товозвращаемоезначениеValueустанавливаетсяравнымединице,чтослужитпризнакомошибки.Обратите внимание на то, что возвращаемое значение из функции Factor() может приниматьлибо значение 1, либо 0, являющееся признаком того, что все прошло нормально, а такжезаметьте,чтофункциявозвращаетэтозначениелишьвстроке40.Итак, искомые значения (квадрат и куб заданного числа) возвращаются в вызывающуюфункциюнепутемиспользованиямеханизмавозвратазначений,азасчетизменениязначенийпеременных,указателикоторыхпереданывфункцию.В строках 36 и 37 посредством указателей переменным в функции main() присваиваютсявозвращаемые значения.
В строке 38 переменной Value присваивается значение возврата,означающееуспешноезавершениеработыфункции.Встроке40этозначениеValueвозвращаетсявызывающейфункции.Этупрограммуможнослегкаусовершенствовать,дополнивееследующимобъявлением:enumERROR_VALUE{SUCCESS,FAILURE};Затемвместовозвратазначений0или1этапрограммасможетвозвращатьSUCCESSИЛИFAILURE.ВозвращениезначенийспомощьюссылокНесмотрянаточтолистинг9.8прекрасноработает,егоможноупроститькакдлячтения,такивэксплуатации,есливместоуказателейиспользоватьссылки.Влистинге9.9показанатажесамаяпрограмма,новместоуказателейвкачествепараметровфункциивнейиспользуютсяссылки,атакжедобавленоупомянутоевышеперечислениеERROR.Листинг9.9.Возвращениезначенийспомощьюссылок1://Листинг9.9.2://Возвращениенесколькихзначенийизфункции3://спомощьюссылок4:5:#include<iostream.h>6:7:typedefunsignedshortUSHORT;8:enumERR_CODE{SUCCESS,ERROR}9:10:ERR_CODEFactor(USHORT,USHORT&,USHORT&);11:12:intmain()13:{14:USHORTnumber,sguared,cubed;15:ERR__CODEresult;16:17:cout<<"Enterаnumber(0-20):";18:cin>>number;19:20:result=Factor(number,squared,cubed);21:22:if(result==SUCCESS)23:{24:cout<<"number:"<<number<<"\n";25:cout<<"square:"<<squared<<"\n";26:cout<<"cubed:"<<cubed<<"\n";27:}28:else29:cout<<"Errorencountered!!\n";30:return0;31:}32:33:ERR_CODEFactor(USHORTn,USHORT&rSquared,USHORT&rCubed)34:{35:if(n>20)36:returnERROR;//simpleerrorcode37:else38:{39:rSquared=n*n;40:rCubed=n*n*n;41:returnSUCCESS;42:}43:}Результат:Enteranumber(0-20):3number:3square:9cubed:27Анализ: Листинг 9.9 идентичен листингу 9.8 с двумя исключениями.
ПеречислениеERR_CODEделаетсообщениеобошибкеболееявным(см.строки36и41),как,впрочем,иегообработку(строка22).Однако более существенные изменения коснулись функции Factor(). Теперь эта функцияобъявляется для принятия не указателей, а ссылок на переменные squared и cubed, что делаетманипуляциинадэтимипараметрамигораздопрощеилегчедляпонимания.ПередачассылокнапеременныекаксредствоповышенияэффективностиПрикаждойпередачеобъектавфункциюкакзначениясоздаетсякопияэтогообъекта.Прикаждомвозвратеобъектаизфункциисоздаетсяещеоднакопия.На занятии 5 вы узнали о том, что эти объекты копируются в стек и на этот процессрасходуется время и память.