246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 63
Текст из файла (страница 63)
В противном случае указатель производного класса становитсянулевым.Примериспользованияэтогоподходапоказанвлистинге13.2.Листинг13.2.Приведениевниз1://Листинг132Использованиеоператораdynamic_cast2://Использованиеrtti3:4:#include<iostreamh>5:enumTYPE{HORSE,PEGASUS};6:7:classHorse8:{9:public10:virtualvoidGallop(){cout<<"Galloping...\n";}11:12:private:13:intitsAge;14:};15:16:classPegasus:publicHorse17:{18:public:19:20:virtualvoidFly(){cout<<"Icanfly!Icanfly!Icanfly!\n";}21:};22:23:constmtNumberHorse=5,24:intmain()25:{26:Horse*Ranch[NumberHorse];27:Horse*pHorse;28:intchoice,i,29:for(i=0;KNumberHorse,i++)30:{31:cout<<"(1)Horse(2)Pegasus:";32:cin>>choice;33:if(choice==2)34:pHorse=newfegasus;35:else36:pHorse=newHorse;37:Rancfi[i]=pHorse,38:}39:cout<<"\n";40:for(i=0;a<NumberHorses;i++)41:{42:Pegasus*pPeg=dynamic_cast<Pegasus*>(Ranch[i]);43:if(pPeg)43:pPeg->Fly();44:else45:cout<<"Justahorse\n";46:47:deleteRanch[i];48:}49:return0;50:}Результат:(1)Horse(2)Pegasus:1(1)Horse(2)Pegasus:2(1)Horse(2)Pegasus:1(1)Horse(2)Pegasus:2(1)Horse(2)Pegasus:1JustahorseIcanfly!Icanfly!Icanfly!JustahorseIcanfly!Icanfly!Icanfly!JustahorseВопросыиответыВо время компиляции появляется сообщение об ошибке C4541: 'dynamic_cast' used onpolymorphictype'classHorse'with/GR-;unpredictablebehaviormayresultКакпоступить?Это сообщение MFC действительно может смутить начинающего программиста.
Чтобыустранитьошибку,выполнитеряддействий.1.ВыберитевокнепроектакомандуProject->Settings.2.ПерейдитеквкладкеС/C++.3.ВыберитевраскрывающемсяспискеCategoryопциюC++Language4.УстановитеEnableRuntimeTypeInformation(RTTI).5.Повторноскомпилируйтевесьпроект.Анализ: Этот пример программы также будет вполне работоспособным. Метод Fly() несвязаннапрямуюсклассомHorseинебудетвызыватьсядляобычныхобъектовэтогокласса.ОнвыполняетсятолькодляобъектовклассаPegasus,нодляэтогопрограммеприходитсякаждыйразанализировать, с каким объектом связан указатель, и приводить текущий указатель к типупроизводногокласса.Всежеследуетпризнать,чтопрограммысприведениемтипаобъектоввыглядятнескольконеуклюжеивнихлегкозапутаться.Крометого,данныйподходидетвразрезсосновнойидеейполиморфизма виртуальных функций, поскольку выполняемость метода теперь зависит отприведениятипаобъектавовремявыполненияпрограммы.ДобавлениеобъектавдваспискаДругая проблема состоит в том, что при объявлении Pegasus как объекта типа Horseстановится невозможным добавить его в список объектов класса Bi rd.
Приходилось топереносить функцию Fly() вверх по иерархии классов, то выполнять приведение указателя, нотакинеудалосьвполноймередостичьнеобходимогофункционированияпрограммы.Таким образом, придерживаясь только одиночного наследования, оказалось невозможнымэлегантнорешитьэтупроблему.Можноперенестивсетрифункции—Fly(),Whinny()иGallop()— в базовый класс Animal, общий для двух производных классов Bird и Horse. В результатевместодвухсписковобъектовдляклассовBirdиHorseполучитсяодинобщийсписокобъектовклассаAnimal.Недостатокметодасостоитвтом,чтобазовыйкласспринимаетнасебяслишкоммногофункций.Вкачествеальтернативыможнооставитьметодытам,гдеониесть,изанятьсяприведениемтиповобъектовклассовHorse,BirdиPegasus,норезультатвконечномитогебудетещехуже!Рекомендуется:Переносите вверх по иерархии классов функции общего использования.Избегайте использовать коды, основанные на определении типов объектов во времявыполнения программы.
Вместо этого используйте виртуальные методы, шаблоны имножественноенаследование.Не рекомендуется:Не переносите вверх по иерархии классов интерфейсы производныхклассов.МножественноенаследованиеСуществует возможность производить новые классы более чем от одного базового класса.Такойпроцессназываетсямножественнымнаследованием.Чтобыпроизвестиподобныйкласс,базовые классы в объявлении разделяются запятыми. В листинге 13.3 класс Pegasus объявлентакимобразом,чтонаследуетсвойствадвухбазовыхклассов—BirdиHorse.ЗатемпрограммадобавляетобъектPegasusвспискиобъектовобоихклассов.Листинг13.3.Множественноенаследование1://Листинг13.3.Множественноенаследование.2://Множественноенаследование3:4:#include<iostream.h>5:6:classHorse7:{8:public:9:Horse(){cout<<"Horseconstructor...";}10:virtual~Horse(){cout<<"Horsedestructor...";}11:virtualvoidWhinny()const{cout<<"Whinny!...";}12:private:13:intitsAge;14:};15:16:classBird17:{18:public:19:Bird(){cout<<"Birdconstructor...";}20:virtual~Bird(){cout<<"Birddestructor...";}21:virtualvoidChirp()const{cout<<"Chirp...";}22:virtualvoidFly()const23:{24:cout<<"Icanfly!Icanfly!Icanfly!";25:}26:private:27:intitsWeight;28:};29:30:classPegasus:publicHorse,publicBird31:{32:public:33:voidChirp()const{Whinny();}34:Pegasus(){cout<<"Pegasusconstructor...";}35:~Pegasus(){cout<<"Pegasusdestructor...";}36:};37:38:constintMagicNumber=2;39:intmain()40:{41:Horse*Ranch[MagicNumber];42:Bird*Aviary[MagicNumber];43:Horse*pHorse;44:Bird*pBird;45:intchoice,i;46:for(i=0;i<MagicNumber;i++)47:{48:cout<<"\n(1)Horse(2)Pegasus:";49:cin>>choice;50:if(choice==2)51:pHorse=newPegasus;52:else53:pHorse=newHorse;54:Ranch[i]=pHorse;55:}56:for(i=0;i<MagicNumber;i++)57:{58:cout<<"\n(1)Bird(2)Pegasus:";59:cin>>choice;60:if(choice==2)61:pBird=newPegasus;62:else63:pBird=newBird;64:Aviary[i]=pBird;65:}66:67:cout<<"\n";68:for(i=0;i<MagicNumber;i++)69:{70:cout<<"\nRanch["<<i<<"]:71:Ranch[i]->Whinny();72:deleteRanch[i];73:}74:75:for(i=0;i<MagicNumber;i++)76:{77:cout<<"\nAviary["<<i<<"]78:Aviary[i]->Chirp();79:Aviary[i]->Fly();80:deleteAviary[i];81:}82:return0;83:}Результат:(1)Horse(2)Pegasus:1Horseconstructor...(1)Horse(2)Pegasus:2Horseconstructor...Birdconstructor...Pegasusconstructor...(1)Bird(2)Pegasus:1Birdconstructor...(1)6ird(2)Pegasus:2Horseconstructor...Birdconstructor...Pegasusconstructor...Ranch[0]:Whinny!...Horsedestructor...Ranch[1]:Whinny!...Pegasusdestructor...Birddestructor...Horsedestructor...Aviary[0]:Chirp...Icanfly!Icanfly!Icanfly!Birddestructor...Aviary[1]:Whinny!...Icanfly!Icanfly!Icanfly!Pegasusdestructor...Birddestructor...Horsedestructor...Анализ: В строках 6—14 объявляется класс Horse.
Конструктор и деструктор выводят наэкрансообщенияосвоейработе,аметодWhinny()печатаетWhinny!(И-го-го).Класс Bird объявляется в строках 16—28. В дополнение к своим конструктору идеструктору этот класс содержит два метода: Chirp() и Fly(), каждый из которых выводит наэкран соответствующие сообщения. В реальных программах эти методы могут воспроизводитьопределенныйзвуковойфайлилиуправлятьанимационнымиэффектаминаэкране.Наконец,встроках30-36объявляетсяклассPegasus.Онпроизводитсясразуотдвухбазовыхклассов—BirdиHorse.ВклассезамешаетсяметодChirp()такимобразом,чтовызываетсяметодWhinny(),которыйунаследованэтимклассомотклассаHorse.Создаетсядвасписка:Ranch(конюшня),которыйвстроке41связываетсясклассомHorse,иAviary (птичник), который в строке 42 связывается с классом Bird.
В строках 46—55 в списокRanch добавляются два объекта — Horse и Pegasus. В строках 56—65 в список AviaryдобавляютсяобъектыBirdиPegasus.Вызовы виртуальных методов с помощью указателей классов Bird и Horse одинакововыполняются для объекта Pegasus. Например, в строке 78 метод Chirp() вызываетсяпоследовательно для всех объектов, указатели на которые представлены в массиве Aviary.ПосколькуэтотметодобъявленвклассеBirdкаквиртуальный,онправильноВыполняетсядлявсехобъектовсписка.По выводимым на экран строкам можно заключить, что при создании объекта Pegasusвызываютсяконструкторывсехтрехклассов—Bird,HorseиPegasus,каждыйизкоторыхсоздаетсвою часть объекта. При удалении объекта также удаляются его части, относящиеся к классамBirdиHorse,длячегодеструкторывэтихклассахобъявленыкаквиртуальные.ОбъявлениемножественногонаследованияЧтобыуказать,чтосоздаваемыйобъектнаследуетсвойстваболеечемодногобазовогоклассапослеименисоздаваемогоклассаставитсядвоеточие,вследзакоторымчереззапятуюперечисленыименавсехбазовыхклассов.Пример1:classPegasus:publicHorse,publicBirdПример2:classSchnoodle:publicSchnauzer,publicPoodleИзкакихчастейсостоятобъекты,полученныеврезультатемножественногонаследованияКогда в памяти компьютера создается объект Pegasus, конструкторы обоих классовпринимаютучастиевегопостроении,какпоказанонарис.13.1.Рис.13.1.Объект,полученныйврезультатемножественногонаследованияВслучаеиспользованиямножественногонаследованиявозникаетряднепростыхивесьмаинтересныхвопросов.Например,чтопроизойдет,еслиобабазовыхклассабудутиметьодноитоже имя либо содержать виртуальные функции или данные с одинаковыми именами? Какинициализируются конструкторы разных базовых классов? Что произойдет, если два базовыхкласса будут произведены от одного и того же родительского класса? Все эти вопросы будутрассмотрены в следующем разделе, после чего можно переходить к практическомуиспользованиюмножественногонаследования.Конструкторыклассов,полученныхврезультатемножественногонаследованияЕсликлассPegasusпроизводитсяотдвухбазовыхклассов—BirdиHorse,авкаждомизнихобъявлены конструкторы со списками параметров, то класс Pegasus инициализирует этиконструкторы.Какэтопроисходит,показановлистинге13.4.Листинг13.4.Созданиеобъектовпримножественномнаследовании1://Листинг13.4.2://Созданиеобьектовпримножественномнаследовании3:#include<iostream.h>4:typedefintHANDS;5:enumCOLOR{Red,Green,Blue,Yellow,White,Black,Brown};6:7:classHorse8:{9:public:10:Horse(COLORcolor,HANDSheight);11:virtual~Horse(){cout<<"Horsedestructor...\n";}12:virtualvoidWhinny()const{cout<<"Whinny!...";}13:virtualHANDSGetHeight()const{returnitsHeight;}14:virtualCOLORGetColor()const{returnitsColor;}15:private:16:HANDSitsHeight;17:COLORitsColor;18:};19:20:Horse::Horse(COLORcolor,HANDSheight):21:itsColor(color),itsHeight(height)22:{23:cout<<"Horseconstructor...\n";24:}25:26:classBird27:{28:public:29:Bird(COLORcolor,boolmigrates);30:virtual~Bird(){cout<<"Birddestructor...\n";}31:virtualvoidChirp()const{cout<<"Chirp...";}32:virtualvoidFly()const33:{34:cout<<"Icanfly!Icanfly!Icanfly!";35:}36:virtualCOLORGetColor()const{returnitsColor;}37:virtualboolGetMigration()const{returnitsMigration;}38:39:private:40:COLORitsColor;41:boolitsMigration;42:};43:44:Bird::Bird(COLORcolor,boolmigrates):45:itsColor(color),itsMigration(migrates)46:{47:cout<<"Birdconstructor...\n";48:}49:50:classPegasus:publicHorse,publicBird51:{52:public:53:voidChirp()const{Whinny();}54:Pegasus(COLOR,HANDS,bool,long);55:~Pegasus(){cout<<"Pegasusdestructor...\n";}56:virtuallongGetNumberBelievers()const57:{58:returnitsNumberBelievers;59:}60:61:private:62:longitsNumberBelievers;63:};64:65:Pegasus::Pegasus(66:COLORaColor,67:HANDSheight,68:boolmigrates,69:longNumBelieve):70:Horse(aColor,height),71:Bird(aColor,migrates),72:itsNumberBelievers(NumBelieve)73:{74:cout<<"Pegasusconstructor...\n";75:}76:77:intmain()78:{79:Pegasus*pPeg=newPegasus(Red,5,true,10);80:pPeg->Fly();81:pPeg->Whinny();82:cout<<"\nYourPegasusis"<<pPeg->GetHeight();83:cout<<"handstalland";84:if(pPeg->GetMigration())85:cout<<"itdoesmigrate.";86:else87:cout<<"itdoesnotmigrate.";88:cout<<"\nAtotalof"<<pPeg->GetNumberBelievers();89:cout<<"peoplebelieveitexists.\n";90:deletepPeg;91:return0;92:}Результат:Horseconstructor...Birdconstructor...Pegasusconstructor...Icanfly!Icanfly!Icanfly!Whinny!...YourPegasusis5handstallanditdoesmigrate.Atotalof10peoplebelieveitexists.Pegasusdestructor...Birddestructor...Horsedestructor...Анализ:КлассHorseобъявляетсявстроках7—18.Конструкторэтогоклассапринимаетдвапараметра: один из них — это перечисление, объявленное в строке 5, а второй — новый тип,объявленный с помощью typedef в строке 4.