246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 43
Текст из файла (страница 43)
В данном примереиспользовалсяконстантныйуказательнаконстантныйобъект,чтопредотвращаловозможностьизменения объекта функцией. Однако по-прежнему имеет место некоторая громоздкостьсинтаксиса,свойственнаяпередачевфункцииуказателей.Поскольку, как вам известно, объект никогда не бывает нулевым, внутреннее содержаниефункции упростилось бы, если бы ей вместо указателя передавалась ссылка. Подтверждениеэтимсловамвынайдетевлистинге9.12.Листинг3.12.Передачассылокнаобъекты1://Листинг9.12.2://Передачассылокнаобъекты3:4:#include<iostream.h>5:6:classSimpleCat7:{8:public:9:SimpleCat();10:SimpleCat(SimpleCat&);11:~SimpleCat();12:13:intGetAge()const{returnitsAge;}14:voidSetAge(intage){itsAge=age:}15:16:private:17:intitsAge;18:};19:20:SimpleCat::SimpleCat()21:{22:cout<<"SimpleCatConstructor...\n";23:itsAge=1;24:}25:26:SimpleCat::SimpleCat(SimploCat&)27:{28:cout<<"SimpleCatCopyCunstructor...\n";29:}30:31:SimpleCat::~SimpleCat()32:{33:cout<<"SimpleCatDestructor...\n";34:}35:36:constSimpleCat&FunctionTwo(constSimpleCat&theCat);37:38:intmain()39:{40:cout<<"Makingacat...\n";41:SimpleCatFrisky;42:cout<<"Friskyis"<<Frisky.GetAge()<<"yearsold\n";43:intage=5;44:Frisky,SetAge(age);45:cout<<"Friskyis"<<Frisky.GetAge()<<"yearsold\n";46:cout<<"CallingFunctionTwo...\n";47:FunctionTwo(Frisky);48:cout<<"Friskyis"<<Frisky.GetAge()<<"yearsold\n";49:return0;50:}51:52://functionTwo,passesareftoaconstobject53:constSimpleCat&FunctionTwo(constSimpleCat&theCat)54:{55:cout<<"FunctionTwo.Returning...\n";56:cout<<"Friskyisnow"<<theCat.GetAge();57:cout<<"yearsold\n";58://theCat.SetAge(8);const!59:returntheCat;60:}Результат:Makingacat...SimpleCatconstructor...Friskyis1yearsoldFriskyis5yearsoldCallingFunctionTwo...FunctionTwo.Returning...Friskyisnow5yearsoldFriskyis5yearsoldSimpleCatDestructor...Анализ: Результат работы этой программы идентичен результату, показанному послелистинга9.11.
Единственное существенное изменение — функция FunctionTwo() теперьпринимает и возвращает ссылки на константный объект. И вновь-таки работа со ссылкаминесколькопроще,чемработасуказателями,хотяприэтомдостигаетсятажеэкономиясредствиэффективность выполнения, а также обеспечивается надежность за счет использованияспецификатораconst.КонстантныессылкиПрограммисты, работающие с языком C++, обычно не видят разницы междуконстантнойссылкойнаобъектSimpleCatиссылкойнаконстантныйобъектSimpleCat.Самиссылки нельзя переназначать, чтобы они ссылались на другой объект, поэтому они всегдаконстантны.
Если к ссылке применено ключевое слово const, то это делает константнымобъект,скоторымсвязанассылка.Когдалучшеиспользоватьссылки,акогда-указателиОпытные программисты безоговорочно отдают предпочтение ссылкам, а не указателям.Ссылкипрощеиспользовать,ионилучшесправляютсясзадачейсокрытияинформации,каквывиделивпредыдущемпримере.Но ссылки нельзя переназначать. Если же вам нужно сначала указывать на один объект, азатем на другой, придется использовать указатель.
Ссылки не могут быть нулевыми, поэтому,если существует хоть какая-нибудь вероятность того, что рассматриваемый объект может бытьнулевым,вамнельзяиспользоватьссылку.Вэтомслучаенеобходимоиспользоватьуказатель.В качестве примера рассмотрим оператор new. Если оператор new не сможет выделитьпамятьдляновогообъекта,онвозвратитнулевойуказатель.Апосколькуссылканеможетбытьнулевой,вынедолжныинициализироватьссылкунаэтупамятьдотехпор,поканепроверите,чтоонаненулевая.Вследующемпримерепоказано,какэтосделать:int*pInt=newint;if(pInt!=NULL)int&rInt=*pInt;ВэтомпримереобъявляетсяуказательpIntназначениетипаint,которыйинициализируетсяобластьюпамяти,возвращаемойоператоромnew.Адресэтойобластипамяти(вуказателеpInt)тестируется, и, если он не равен значению null, указатель pInt разыменовывается.
Результатразыменования переменной типа int представляет собой объект типа int, и ссылка rIntинициализируется этим объектом. Следовательно, ссылка rInt становится псевдонимом дляпеременнойтипаint,возвращаемойоператоромnew.Рекомендуется:Передавайте функциям параметры как ссылке везде, где это возможно.Обеспечивайте возврат значений как ссылок везде, где это возможно. Используйтеспецификаторconstдлязащитыссылокиуказателейвезде,гдеэтовозможно.Не рекомендуется: Не используйте указатели, если вместо них можно использоватьссылки.Невозвращайтессылкиналокальныеобъекты.КоктейльизссылокиуказателейНе будет ошибкой в списке параметров одной функции объявить как указатели, так иссылки,атакжеобъекты,передаваемыекакзначения,например:CAT*SomeFunction(Person&theOwner,House*theHouse,intage);Это объявление означает, что функция SomeFunction принимает три параметра. ПервыйявляетсяссылкойнаобъекттипаPerson,второй—указателемнаобъекттипаHouse,атретий—целочисленнымзначением.СамажефункциявозвращаетуказательнаобъектклассаCAT.Следует также отметить, что при объявлении соответствующих переменных можноиспользовать разные стили размещения операторов ссылки (&) и косвенного обращения (*).Вполнезаконнойбудетлюбаяизследующихзаписей:1:CAT&rFrisky;2:CAT&rFrisky;3:CAT&rFrisky;Примечание:Символы пробелов в программах на языке C++ полностью игнорируются,поэтомувезде,девывидитепробел,можноставитьнесколькопробелов,символовтабуляцииилисимволовразрывовстрок.Оставиввпокоевопросысвободноговолеизъявления,попробуемразобраться в том, какой вариант все же лучше других.
Как ни странно, можно найтиаргументы в защиту каждого из трех вариантов. Аргумент в защиту первого вариантасостоит в следующем. rFrisky — это переменная с именем rFrisky, тип которой можноопределить как ссылку на объект класса CAT. Поэтому вполне логично, чтобы оператор &стоялрядомстипом.Однакоестьиконтраргумент.CAT—этотип.Оператор&являетсячастьюобъявления,которое включает имя переменной и амперсант. Но следует отметить, что слияние вместесимвола&иименитипаCATможетпривестиквозникновениюследующейошибки:CAT&rFrisky,rBoots;Поверхностный анализ этой строки может натолкнуть на мысль, что как переменнаяrFrisky,такипеременнаяrBootsявляютсяссылкаминаобъектыклассаCAT.Однакоэтонетак.Насамомделеэтообъявлениеозначает,чтоrFriskyявляетсяссылкойнаобъектклассаCAT,аrBoots (несмотря на свое имя с характерным префиксом) — не ссылка, а обыкновеннаяпеременнаятипаCAT.Поэтомупоследнееобъявлениеследуетпереписатьпо-другому:CAT&rFrisky,rBoots;Вответнаэтовозражениестоитпорекомендовать,чтобыобъявленияссылокиобычныхпеременныхникогданесмешивалисьводнойстроке.Вотправильныйварианттойжезаписи:CAT&rFrisky;CATBoots;Примечание:Наконец, многие программисты не обращают внимания на приведенныеаргументыи,считая,чтоистинанаходитсяпосередине,выбираютсреднийвариант(средний,кстати,вдвухсмыслах),которыйиллюстрируетсяслучаем2:2:CAT&rFrisky;Безусловно,всесказанноедосихпоробоператорессылки(&)относитсявравнойстепении к оператору косвенного обращения (<<).
Выберите стиль, который вам подходит, ипридерживайтесь его на протяжении всей программы, ведь ясность текста программы —однаизосновныхсоставляющихуспеха.Многие программисты при объявлении ссыпок и указателей предпочитаютпридерживатьсядвухсоглашений.1. Размещать амперсант или звездочку посередине, окаймляя этот символ пробелами сдвухсторон.2. Никогда не объявлять ссылки, указатели и переменные в одной и той же строкепрограммы.Невозвращайтессылкунаобъект,которыйнаходитьсявнеобластивидимости!Научившись передавать аргументы как ссылки на объекты, программисты порой теряютчувство реальности.
Не стоит забывать, что все хорошо в меру. Помните, что ссылка всегдаслужит псевдонимом некоторого объекта. При передаче ссылки в функцию или из нее незабудьте задать себе вопрос: "Что представляет собой объект, псевдонимом которого яманипулирую,ибудетлионсуществоватьвмоментегоиспользования?"Влистинге9.13показанпримервозможнойошибки,когдафункциявозвращаетссылкунаобъект,которогоуженесуществует.Листинг9.13.Возвращениессылкинанесуществующийобъект1://Листинг9.13.2://Возвращениессылкинаобъект,3://которогобольшенесуществует4:5:#include<iostream.h>6:7:classSimpleCat8:{9:public:10:SimpleCat(intage,intweight);11:~SimpleCat(){}12:intGetAge()<returnitsAge;}13:intGetWeight(){returnitsWeight;}14:private:15:intitsAge;16:intitsWeight;17:};18:19:SimpleCat::SimpleCat(intage,intweight)20:{21:itsAge=age;22:itsWeight=weight;23:}24:25:SimpleCat&TheFunction();26:27:intmain()28:{29:SimpleCat&rCat=TheFunction();30:intage=rCat.GetAge();31:cout<<"rCat"<<age<<"yearsold!\n"32:return0;33:}34:35:SimpleCat&TheFunction()36:{37:SimpleCatFrisky(5,9);38:returnFrisky;39:}Результат:Compileerror:Attemptingtoreturnareferencetoalocalobject!(Ошибкакомпиляции:попыткавозвратитьссылкуналокальныйобъект!)Предупреждение:Эта программа не компилируется на компиляторе фирмы Borland, нодля нее подходят компиляторы компании Microsoft.
Однако профессиональный программистникогданестанетполагатьсянауступкикомпилятора.Анализ: В строках 7—17 объявляется класс SimpleCat. В строке 29 инициализируетсяссылканаобъектклассаSimpleCatсиспользованиемрезультатоввызовафункцииTheFunction(),объявленнойвстроке25.СогласнообъявлениюэтафункциявозвращаетссылкунаобъектклассаSimpleCat.В теле функции TheFunction() объявляется локальный объект типа SimpleCat иинициализируется его возраст и вес. Затем этот объект возвращается по ссылке.