246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 64
Текст из файла (страница 64)
Этот конструктор выполняется в строках 20—24.При этом инициализируется одна переменная-член и на экран выводится сообщение о работеконструктораклассаHorse.Встроках26—42объявляетсяклассBird,конструкторкотороговыполняетсявстроках45—49. Конструктор этого класса также принимает два параметра. Обратите внимание наинтересный факт: конструкторы обоих классов принимают перечисления цветов, с помощьюкоторых в программе можно установить цвет лошади или цвет перьев у птицы. В результате,когдавыпопытаетесьустановитьцветПегаса,можетвозникнутьпроблемавработепрограммы,котораяобсуждаетсянесколькониже.Класс Pegasus объявляется в строках 50—63, а его конструктор — в строках 65—75.ИнициализацияобъектаPegasusвыполняетсятремястрокамипрограммы.Сначалаконструкторкласса Horse определяет цвет и рост.
Затем конструктор класса Bird инициализируется цветомперьев и логической переменной. Наконец, происходит инициализация переменной-членаitsNumberBelievers, относящейся к классу Pegasus. После всех этих операций вызываетсяконструкторклассаPegasus.В функции main() создается указатель на класс Pegasus, который используется дляполучениядоступакфункциям-членамбазовыхобъектов.ДвусмысленностьситуацииВлистинге13.4обакласса—HorseиBird—имеютметодGetColor().Впрограммеможетпотребоваться возвратить цвет объекта Pegasus, но возникает вопрос: какой из двухунаследованных методов при этом будет использоваться? Ведь методы, объявленные в обоихбазовых классах, имеют одинаковые имена и сигнатуры.
В результате при компилированиипрограммывозникнетнеопределенность,которуюнеобходиморазрешитьдокомпиляции.Еслипростозаписать:COLORcurrentColor=pPeg->GetColor();Компилятор покажет сообщение об ошибке Member is ambiguous: ' Horse::GetColor' and 'Bird::GetColor'(Членнеопределен).Этунеопределенностьможноразрешить,явнообратившиськметодутогокласса,которыйвамнеобходим:COLORcurrentColor=pPeg->Horse::GetColor();В любом случае при возникновении подобной ситуации, когда требуется сделать выбормежду одноименными методами или переменными-членами разных классов, следует явноуказыватьимянеобходимогобазовогоклассапередименемфункции-членаилипеременной.Если в классе Pegasus эта функция будет замещена, то проблема решится сама собой, таккаквэтомслучаевызываетсяфункция-членклассаPegasus:virtualCOLORGetColor()const{returnHorse::GetColor();}Такимобразом,проблемунеопределенностиможнообойтиблагодаряинкапсуляцииявногоуказания базового класса в объявлении замещенной функции.
Если возникнет необходимостьиспользовать метод другого класса, то обращение к нему с помощью приведенного нижевыражениянебудетошибкой.COLORcurrentColor=pPeg->Bird::GetColor();НаследованиеотобщегобазовогоклассаЧтопроизойдет,еслиобабазовыхкласса,откоторыхпроизводитсядругойкласс,самибылипроизведены от одного общего базового класса, как, например, классы Bird и Horse от классаAnimal.Этаситуацияпоказананарис.13.2.Рис.13.2.ОбщийбазовыйклассКак показано на рис.
13.2, два класса, являющихся базовыми для класса Pegasus, самипроизводятсяотодногообщегоклассаAnimal.КомпиляторприэтомрассматриваетклассыBirdиHorseкакпроизводныеотдвуходноименныхбазовыхклассов,чтоможетпривестикочереднойнеопределенности.Например,есливклассеAnimalобъявленыпеременная-членitsAgeифункция-членGetAge(),авпрограммеделаетсявызовpGet->GetAge(),то будет ли при этом вызываться функция GetAge(), унаследованная классом Bird от классаAnimalиликлассомHorseотбазовогокласса?Этопротиворечиеразрешаетсявлистинге13.5.Листинг13.5.Общийбазовыйкласс1://Листинг13.5.2://Общийбазовыйкласс3:#include<iostream.h>4:5:typedefintHANDS;6:enumCOLOR{Red,Green,Blue,Yellow,White,Black,Brown}7:8:classAnimal//общийбазовыйклассдляклассовhorseиbird9:{10:public:11:Animal(int);12:virtual~Animal(){cout<<"Animaldestructor...\n";}13:virtualintGetAge()const{returnitsAge;}14:virtualvoidSetAge(intage){itsAge=age;}15:private:16:intitsAge;17:};18:19:Animal::Animal(intage):20:itsAge(age)21:{22:cout<<"Animalconstructor...\n";23:}24:25:classHorse:publicAnimal26:{27:public:28:Horse(COLORcolor,HANDSheight,intage);29:virtual~Horse(){cout<<"Horsedestructor...\n";}30:virtualvoidWhinny()const{cout<<"Whinny!...";}31:virtualHANOSGetHeight()const{returnitsHeight;}32:virtualCOLORGetColor()const{returnitsColor;}33:protected:34:HANDSitsHeight;35:COLORitsColor;36:};37:38:Horse::Horse(C0L0Rcolor,HANDSheight,intage):39:Animal(age),40:itsColor(color),itsHeight(height)41:{42:cout<<"Horseconstructor...\n";43:}44:45:classBird:publicAnimal46:{47:public:48:Bird(COLORcolor,boolmigrates,intage);49:virtual~Bird(){cout<<"Birddestructor...\n";}50:virtualvoidChirp()const{cout<<"Chirp...";}51:virtualvoidFly()const52:{cout<<"Icanfly!Icanfly!Icanfly!";}53:virtualC0L0RGetColor()const{returnitsColor;}54:virtualboolGetMigration()const{returnitsMigration;}55:protected:56:COLORitsColor;57:boolitsMigration;58:};59:60:Bird::Bird(COLORcolor,boolmigrates,intage):61:Animal(age),62:itsColor(color),itsMigration(migrates)63:{64:cout<<"Birdconstructor...\n";65:}66:67:classPegasus:publicHorse,publicBird68:{69:public:70:voidChirp()const{Whinny();}71:Pegasus(COLOR,HANDS,bool,long,int);72:virtual~Pegasus(){cout<<"Pegasusdestructor...\n";}73:virtuallongGetNumberBelievers()const74:{returnitsNumberBelievers;}75:virtualCOLORGetColor()const{returnHorse::itsColor;}76:virtualintGetAge()const{returnHorse::GetAge();}77:private:78:longitsNumberBelievers;79:};80:81:Pegasus::Pegasus(82:COLORaColor,83:HANDSheight,84:boolmigrates,85:longNumBelieve,86:intage):87:Horse(aColor,height,age),88:Bird(aColor,migrates,age),89:itsNumberBelievers(NumBelieve)90:{91:cout<<"Pegasusconstructor...\n";92:}93:94:intmain()95:{96:Pegasus*pPeg=newPegasus(Red.5,true,10,2);97:intage=pPeg->GetAge();98:cout<<"Thispegasusis"<<age<<"yearsold.\n";99:deletepPeg;100:return0;101:}Результат:Animalconstructor...Horseconstructor...Animalconstructor...Birdconstructor...Pegasusconstructor...Thispegasusis2yearsold.Pegasusdestructor.,.Birddestructor...Animaldestructor...Horsedestructor...Animaldestructor...Анализ:Влистингесодержитсярядинтересныхрешений.Так,встроках8—17объявляетсяновыйклассAnimalспеременной-членомitsAgeидвумяметодами—GetAge()иSetAge().В строке 25 класс Horse производится от класса Animal.
Конструктор класса Horse теперьимееттретийпараметрage,которыйпередаетсявбазовыйклассAnimal.Обратитевнимание,чтовклассеHorseметодGetAge()незамещается,апростонаследуется.В строке 46 класс Bird производится от класса Animal. Конструктор этого класса такжесодержит параметр age, с помощью которого инициализируется базовый класс Animal. МетодGetAge()такженаследуетсяэтимклассомбеззамещения.Класс Pegasus производится от двух базовых классов Horse и Bird, поэтому с исходнымбазовымклассомAnimalонсвязандвумялинияминаследования.ЕслидляобъектаклассаAnimalбудет вызван метод GetAge(), то для преодоления неопределенности нужно точно указать, ккакому базовому классу следует обращаться за этим методом, либо метод GetAge() следуетзаместитьвклассеPegasus.ВнашемпримерепрограммыметодGetAge()замещаетсядляклассаPegasusтакимобразом,чтовнемявноуказываетсяобращениеканалогичномуметодуконкретногобазовогокласса.Замещениефункциисдобавлениемобращениякметодубазовогоклассапозволяетрешитьдвепроблемы.Во-первых,преодолеваетсянеопределенностьобращениякбазовымклассам;вовторых,функциюможнозаместитьтакимобразом,чтовпроизводномклассеприобращениикэтойфункциибудутвыполнятьсядополнительныеоперации,которыхнебыловбазовомклассе.Причемпожеланиюпрограммистаэтидополнительныеоперациимогутвыполнятьсядовызовафункции базового класса или после вызова с использованием значения, возвращенногофункциейбазовогокласса.Конструктор класса Pegasus принимает пять параметров: цвет крылатого коня, его рост (вфутах);логическуюпеременную,котораяопределяет,мигрируетсейчасэтоживотноеилимирнопасетсянапастбище;числолюдей,верящихвсуществованиеПегаса,ивозрастживотного.Встроке87конструкторинициализируетпеременные,определенныевклассеHorse(цвет,ростивозраст).Вследующейстрокеинициализируетсячасть,относящаясяк классу Bird: цвет, миграции и возраст.
Наконец, в строке 89 инициализируется переменнаяitsNumberBelievers,относящаясянепосредственнокклассуPegasus.ВызовконструктораклассаHorseвстроке87выполняетоператоры,записанныевстроке38.С помощью параметра age конструктор класса Horse инициализирует переменную itsAge,унаследованную классом Horse от класса Animal. Затем инициализируются две переменныечленаклассаHorse—itsColorиitsHeight.ВызовконструктораклассаBirdвстроке88выполняетоператоры,записанныевстроке60.И в данном случае параметр age используется для инициализации переменной-члена,унаследованнойклассомBirdотклассаAnimal.Обратите внимание, что значение параметра цвета объекта Pegasus используется дляинициализациисоответствующихпеременных-членовобоихклассов,BirdиHorse.ПараметрageтакжеинициализируетпеременнуюitsAgeобоихэтихклассов,унаследованнуюимиотбазовогоклассаAnimal.ВиртуальноенаследованиеВлистинге13.5решаласьпроблеманеопределенности,аименно:откакогобазовогоклассаунаследована функция getAge() в объекте класса Pegasus.
Но в действительности этот методпроизводитсяотодногообщегобазовогоклассаAnimal.В C++ существует возможность указать, что мы имеем дело не с двумя одноименнымиклассами,какпоказановрис.13.2,асоднимобщимбазовымклассом(рис.13.3).Рис.13.3.ВиртуальноенаследованиеДля этого класс Animal нужно объявить как виртуальный базовый класс для двухпроизводных классов, Horse и Bird. Класс Animal при этом не подвергается никакимизменениям.ВклассахHorseиBirdизменениясостоятвтом,чтовихобъявленииуказываетсявиртуальностьнаследованияотбазовогоклассаAnimal.КлассPegasusизменяетсясущественно.Обычно конструктор класса инициализирует только собственные переменные ипеременные-члены базового класса.