246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 42
Текст из файла (страница 42)
Для таких маленьких объектов, как базовые целочисленныезначения,ценаэтихрасходовнезначительна.Однако для больших объектов, создаваемых пользователем, расходы ресурсов существенновозрастают. Размер такого объекта в стеке представляет собой сумму всех его переменныхчленов. Причем каждая переменная-член может быть, в свою очередь, подобным объектом,поэтомупередачатакоймассивнойструктурыпутемкопированиявстекможетоказатьсявесьмадорогимудовольствиемкакповремени,такипозанимаемойпамяти.Кроме того, существуют и другие расходы. При создании временных копий объектовклассов для этих целей компилятор вызывает специальный конструктор-копировщик. ,Haследующемзанятиивыузнаете,какработаютконструкторы-копировщикиикакможносоздатьсобственныйконструктор-копировщик,нопокадостаточнознать,чтоконструктор-копировщиквызываетсякаждыйраз,когдавстекпомещаетсявременнаякопияобъекта.При разрушении временного объекта, которое происходит при возврате из функции,вызываетсядеструкторобъекта.Еслиобъектвозвращаетсяфункциейкакзначение,копияэтогообъектадолжнабытьсначаласоздана,азатемразрушена.При работе с большими объектами эти вызовы конструктора и деструктора могут оказатьслишком ощутимое влияние на скорость работы программы и использование памятикомпьютера.
Для иллюстрации этой идеи в листинге 9.10 создается пользовательский объектSimpleCat. Реальный объект имел бы размеры побольше и обошелся бы дороже, но и этогопримера вполне достаточно, чтобы показать, насколько часто вызываются конструкторкопировщикидеструктор.Итак, в листинге 9.10 создается объект SimpleCat, после чего вызываются две функции.Первая функция принимает объект Cat как значение, а затем возвращает его как значение.Втораяжефункцияпринимаетуказательнаобъект,анесамобъект,ивозвращаетуказательнаобъект.Листинг9.10.Передачаобъектовкакссылокспомощьюуказателей1://Листинг9.10.2://Передачауказателейнаобъекты3:4:#include<iostream.h>5:6:classSimpleCat7:{8:public:9:SimpleCat();//конструктор10:SimpleCat(SimpleCat&);//конструктор-копировщик11:~SimpleCat();//деструктор12:};13:14:SimpleCat::SimpleCat()15:{16:cout<<"SimpleCatConstructor...\n";17:}18:19:SimpleCat::SimpleCat(SimpleCat&)20:{21:cout<<"SimpleCatCopyConstructor...\n";22:}23:24:SimpleCat::~SimpleCat()25:{26:cout<<"SimpleCatDestructor...\n";27:}28:29:SimpleCatFunction0ne(SimpleCattheCat);30:SimpleCat*FunctionTwo(SimpleCat*theCat);31:32:intmain()33:{34:cout<<"Makingacat,.,\n";35:SimpleCatFrisky;36:cout<<"CallingFunctionOne,,,\n";37:FunctionOne(Frisky);38:cout<<"CallingFunctionTwo..,\n";39:FunctionTwo(&Frisky);40:return0;41:}42:43://ФункцияFunctionOne,передачакакзначения44:SimpleCatFunctionOne(SimpleCattheCat)45:{46:cout<<"FunctionOne.Roturning,,,\ri";47:returntheCat;48:}49:50://ФункцияFunctionTwo,передачакакссылки51:SimpleCat*FunctionTwo(SimpleCat*theCat)52:{53:cout<<"FunctionTwo.Returning...\n";54:returntheCat;55:}Результат:Makingacat...SimpleCatConstructor...CallingFunctionOne...SimpleCatCopyConstructor...FunctionOne.Returning...SimpleCatCopyConstructor...SimpleCatDestructor...SimpleCatDestructor...CallingFunctionTwo...FunctionTwo.Returning...SimpleCatDestructor...Примечание:Номера строк не выводятся.
Мы добавили их для удобства проведенияанализапрограммы.Анализ: В строках 6-12 объявляется весьма упрошенный класс SimpleCat. Конструктор,конструктор-копировщик и деструктор — все компоненты класса выводят на экран своиинформативныесообщения,чтобыбылоточноизвестно,когдаонивызываются.В строке 34 функция main() выводит свое первое сообщение, которое является первым и врезультатахработыпрограммы.Встроке35создаетсяэкземпляробъектаклассаSimpleCat.Этоприводитквызовуконструктора,чтоподтверждаетсообщение,выводимоеэтимконструктором(строка2врезультатахработыпрограммы).Встроке36функцияmain()"докладывает"овызовефункцииFunctionOne,котораявыводитсвое сообщение (строка 3 в результатах работы программы). Поскольку функция FunctionOne()вызываетсяспередачейобъектаклассаSimpleCatпозначению,встекпомещаетсякопияобъектаSimpleCatкаклокальногодлявызываемойфункции.Этоприводитквызовуконструкторакопии,который"вноситсвоюлепту"врезультатыработыпрограммы(сообщениевстроке4).Выполнение программы переходит к строке 46, которая принадлежит телу вызваннойфункции, выводящей свое информативное сообщение (строка 5 в результатах работыпрограммы).
Затем эта функция возвращает управление программой вызывающей функции, иобъект класса SimpleCat вновь возвращается как значение. При этом создается еще одна копияобъекта за счет вызова конструктора-копировщика и, как следствие, на экран выводитсяочередноесообщение(строка6врезультатахработыпрограммы).Значение, возвращаемое из функции FunctionOne(), не присваивается ни одному объекту,поэтому ресурсы, затраченные на создание временного объекта при реализации механизмавозврата, просто выброшены на ветер, как и ресурсы, затраченные на его удаление с помощьюдеструктора, который заявил о себе в строке 7 в результатах работы программы. Посколькуфункция FunctionOne() завершена, локальная копия объекта выходит за область видимости иразрушается, вызывая деструктор и генерируя тем самым сообщение, показанное в строке 8 врезультатахработыпрограммы.Управление программой возвращается к функции main(), после чего вызывается функцияFunctionTwo(),нонаэтотразпараметрпередаетсякакссылка.Приэтомникакойкопииобъектане создается, поэтому отсутствует и сообщение от конструктора- копировщика.
В функцииFunctionTwo()выводитсясообщение,занимающеестроку10врезультатахработыпрограммы,азатем выполняется возвращение объекта класса SimpleCat (снова как ссылки), поэтому нетникакихобращенийкконструкторуидеструктору.Наконец программа завершается и объект Frisky выходит за область видимости, генерируяпоследнее обращение к деструктору, выводящему свое сообщение (строка 11 в результатахработыпрограммы).Проанализировав работу этой программы, можно сделать вывод, что при вызове функцииFunctionOne() делается два обращения к конструктору копии и два обращения к деструктору,посколькуобъектвэтуфункциюпередаетсякакзначение,втовремякакпривызовефункцииFunctionTwo()подобныхобращенийнеделается.ПередачаконстантногоуказателяНесмотря на то что передача указателя функции FunctionTwo() более эффективна, чемпередачапозначению,онатаитвсебенемалуюопасность.ПривызовефункцииFunctionTwo()совершенно не имелось в виду, что разрешается изменять передаваемый ей объект классаSimpleCat, задаваемый в виде адреса объекта SimpleCat.
Такой способ передачи открываетобъект для изменений и аннулирует защиту, обеспечиваемую при передаче объекта какзначения.Передачу объектов как значений можно сравнить с передачей музею фотографии шедевравместо самого шедевра. Если какой-нибудь хулиган испортит фотографию, то никакого вредапри этом оригиналу нанесено не будет. А передачу объекта как ссылки можно сравнить спередачеймузеюсвоегодомашнегоадресаиприглашениемгостейпосетитьвашдомивзглянутьввашемприсутствиинадрагоценныйшедевр.Решить проблему можно, передав в функцию указатель на константный объект классаSimpleCat.Вэтомслучаекданномуобъектумогутприменятьсятолькоконстантныеметоды,неимеющиеправнаизменениеобъектаSimpleCat.Этаидеядемонстрируетсявлистинге9.11.Листинг9.11.Передачаконстантныхуказателей1://Листинг9.11.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(SimpleCat&)27:{28:cout<<"SimpleCatCopyConstructor...\n";29:}30:31:SimpleCat::~SimpleCat()32:{33:cout<<"SimpleCatDestructor...\n";34:}35:36:constSimpleCat*constFunctionTwo(constSimpleCat*consttheCat);37:38:intmain()39:{40:cout<<"Makingаcat...\n";41:SimpleCatFrisky;42:cout<<"Friskyis";43:cout<<Frisky.GetAge();44:cout<<"yearsold\n";45:intage=5:46:Frisky.SetAge(age);47:cout<<"Friskyis";48:cout<<Frisky.GetAge();49:cout<<"yearsold\n";50:cout<<"CallingFunctionTwo...\n";51:FunctionTwo(&Frisky);52:cout<<"Friskyis";53:cout<<Frisky.GetAge();54:cout<<"years_ald\n";55:rsturn0;56:}57:58://functionTwo,passesaconstpointer59:constSimpleCat*constFunctionTwo(constSimpleCat*consttheCat)60:{61:cout<<"FunctionTwo,Returning...\n";62:cout<<"Friskyisnow"<<theCat->GetAge();63:cout<<"yearsold\n";64://theCat->SotAge(8):const!65:returntheCat;66:}Результат:Makingacat...SimpleCatconstructor...Friskyis1yearsoldFriskyis5yearsoldCallingFunctionTwo...FunctionTwo.Returning...Friskyisnow5yearsoldFriskyis5yearsoldSimpleCatDestructor...Анализ:ВклассSimpleCatбылидобавленыдвефункциидоступакданным:методGetAge()(строка13),которыйявляетсяконстантнойфункцией,иметодSetAge()(строка14),которыйнеявляетсяконстантным.Вэтотклассбылатакжедобавленапеременная-членitsAge(строка17).Конструктор, конструктор-копировщик и деструктор по-прежнему определены для выводана экран своих сообщений.
Однако конструктор-копировщик ни разу не вызывался, посколькуобъектбылпереданкакссылкаипоэтомуникакихкопийобъектанесоздавалось.Встроке41былсозданобъектсозначениемвозраста,заданнымпоумолчанию.Этозначениевыводитсянаэкранвстроке42.В строке 46 переменная itsAge устанавливается с помощью метода доступа SetAge, арезультат этой установки выводится на экран в строке 47. В этой программе функцияFunctionOneнеиспользуется,новызываетсяфункцияFunctionTwo(),котораяслегкаизменена.Ееобъявление занимает строку 36.
На этот раз и параметр, и значение возврата объявляются какконстантныеуказателинаконстантныеобъекты.Посколькуипараметр,ивозвращаемоезначениепередаютсякакссылки,никакихкопийнесоздаетсяиконструктор-копировщикневызывается.ОднакоуказательвфункцииFunctionTwo()теперь является константным, следовательно, к нему не может применяться неконстантныйметод SetAge(). Если обращение к методу SetAge() в строке 64 не было бы закомментировано,программанепрошлабыэтапкомпиляции.Обратитевнимание,чтообъект,создаваемыйвфункцииmain(),неявляетсяконстантнымиобъектFriskyможетвызватьметодSetAge().АдресэтогообычногообъектапередаетсяфункцииFunctionTwo(), но, поскольку в объявлении функции FunctionTwo() заявлено, что передаваемыйуказатель должен быть константным указателем на константный объект, с этим объектомфункцияобращаетсятак,какеслибыонбылконстантным!СсылкивкачествеальтернативыПри выполнении программы, показанной в листинге 9.11, устранена необходимостьсоздания временных копий, что сокращает число обращений к конструктору и деструкторукласса, в результате чего программа работает более эффективно.