Г. Шилдт - Полный справочник по C++ (1109478), страница 70
Текст из файла (страница 70)
Рассмотрим еше один вариант предыдущей программы. /* В этой программе для вызова виртуальной функции применяется ссылка на объект базового класса. */ Мьпс1цс)е <ьов7 геазя> цв1пц памеврасе всс); с1авв Ьаве рцЬ11с: чьггца1 чей чьцпс() ( соцс « "Функция чГцпс(7 нз класса Ьаве. 1п"; )г с1авв с)ет1чег)1: рцЫфс Ьаве ( рцЫ(с: чоьб ч1игс () соцс « " Функция чгипс О из класса дет1чед1. 1п"; ) ); с1авв с)етфчег)2: рцЬ11с Ьаве ( рцЬ11с: чофс) чГцпс(7 ( Часть И. Язык С++ сове « " Функция чгцпс() из класса бегхчеб2.1п"; ) ); // Используется параметр, являппийся ссылкой на объект // базового класса. чоЫ б(саве ах) г.чхцпс()г гпс ваап() ( Ьаве Ь; бех1чеб1 61г х)еххчед2 б2; Й(Ь)г // Функции 1() гередается объект класса Ьаве.
х(61); // Функции Г() передается объект класса х)еххчед1. ЕЫ2); // Функции х() передается объект класса дегхчеб2. хесцхп О; ) Зта программа выводит на зкран те же сообщения, что и ранее. В лапном примере функция л() получает в качестве параметра ссылку на объект класса ьаве. В функции ваап() эта фунКцИЯ вЫЗЫвастея С ПОмощью Объектов классов Ьаве, аетхчеа1 и аетхчеаз. конкретныи вариант функции чецпс() выбирается внутри функции й() в зависимости от типа ее параметра. Для простоты в остальных примерах атой главы виртуальные функции вызываются с помошью указателей. причем результат ничем не отличается от вызова с помошью ссылок. ""Й Атрибут агфа! наследуется При наслеловании виртуальной функции ее виртуальная природа также наследуется.
Зто значит, что если производный класс, унаслеловавший виртуальную функцию от базового класса, становится базовым по отношению к другому производному классу, виртуальная функция может по-прежнему замешаться. Иначе говоря, не имеет значения, сколько раз наследовалась виртуальная функция, она все равно остается виртуальной. Рассмотрим следующую программу. Вхпс1цх)е сховххеаи> цвхпо патеврасе всгЬ с1авв Ьазе ( рцЫ хе: ч1х'сца1 чоЫ чхцпс() ( соцс « "Функция чгцпс() из класса Ьаве.1п"; ) ); с1авв х)еххчех)1: рцЬ1хс Ьаве ( рцЬ1хс: чоЫ чбцпс() ( соне сс Функция чбцлс() из класса с)егхчех(1. 1п"; ) Глава 17.
Виртуальные функции и полиморфизм а: /" к~ асс йетачей2 наследует виртуальную Функцию чЕцпс() от класса йетачей1. */ , с)аяя йетзчей2 : рцЫЕс йегтчей1 рцЬ)ъс: // Функция чЕцпс() остается виртуальной. чотй чЕьпс() соце « " Функция чЕцпс() из класса йетъчей2.1п": "': ) ' ); 1пс яа1п[) Ьаяе *р, Ь; йетачей) й1; йегзчей2 й2; Указатель на объект класса Ьазе. р — яЬ; р->чгцпс(): // Вызов Функции чЕцпс() из класса Ьаяе. // Указатель на объект класса йетгчей1 р = ай1г р->чЕцпс(); // Бызов Функции чЕцпс() из класса йег1чей1. // Указатель на класс йеггчей2 р .—. ай2( р-ьчЕцпс(); // Вызов Функции чЕцпс() из класса йеттчей2. юеецтп 0; ) Как и ожидалось, программа выводит на зкран следующие сообщения.
Вызов Функции чЕьпс() из класса Ьаяе. Вызов Функции чЕцпс() из класса йет1чей1. Вызов Функции чЕцпс() из класса йетзчей2. В данном случае класс йет1чейз является наследником класса йетечей1, а не класса Ьаве, но функция чацпс () остается виртуальной. Й Виртуальные функции являются иерархическими Как известно, если функция объявлена виртуальной в базовом классе, ее можно заместить в производном классе.
Однако виртуальную функцию нс обязательно замешать. В зтом случае вызывается функция, определенная в базовом классе. Рассмотрим в качестве примера программу, в которой квасс йет1.гей2 не замещает функцию чацпс(), З1пс1ийе <1ояетеаюь цвтпп павеярасе яей; с1аяз Ьаяе ( рцЫЕс: чаггца1 чотй чЕцпс() ( соце « "Функция чЕцпс() из класса Ьазе. 1п"г Часть (!.
Язык С++ с1авз йехйчей1: рцЬ1зс Ьазе рцЫ з.с: чо1й чбцпс() ( соме « " Функция чбцпс() из класса йех1чей1. 1п" з ) с1авз йехйчей2: рцЫ1с Ьаве (лзЫ1с: // Функция чбцпс() не замещается в классе йехьчей2, // используется версия из класса Ьазе. 1пе таз.п() Ьаве *р, Ьз йех1чей1 й1з йехьчей2 й2; // Указатель на объект класса Ьаве. р = аЬз р->чуппс О з // Вызов Функции чбцпс() из класса Ьаве. // Указатель на объект класса йех1чей1.
р = ай1з р->чуцпс() з // Вызов функции чбцпс() из класса йехзчей1. // Указатель на объект класса йехйчей2. р = йй2з р->чуцпс()з // Вызов функции чуцпс() из класса Ьаве. хеецхп Оз ) Эта программа выводит на экран следуюшие сообщения. | Функция чбцпс() из класса Ьаве. Функция чуцпс() из класса йех1чей1. Функция чуцпс() из класса Ьаве. Поскольку функция чеппс О не замещается в классе йех1чейа, через указатель на объект класса йехйззей2 вызывается ее версия из класса ьаве. Предыдущая программа иллюстрирует частный случай более универсального правила Наследование в языке С++ организовано по иерархическому принципу, поэтому виртуальные функции также должны быть иерархическими.
Это значит, что если виртуальная функция не замещается, вызывается ее предыдущая переопределенная версия. Например, в следующей программе класс йехйчейа является наследником класса йех1чей1, который, в свою очередь, является производным от класса Ьаве. Однако функция чеппс О в классе йех1чейа не замещается. Следовательно, ближайшая к классу йех1чейа версия функции чязьпс О определена в классе йех1чей1. Таким образом, вызов функции чеипс() с помощью объекта класса йех1чейа относится к функции йех1чей1з зчяппс().
В1пс1цйе <1озсхеащ> цвьпд пащезрасе зсй( с1азв Ьаве ( рцЫз.с з ч1хсца1 чозй чбцпс() ( Глава ) 7. Виртуальные функции н полиморфизм соне « "Функция чбцпс() из класса Ьаве.лп" т )у с1аяв йелйчей1 : рцЫйс Ьаве ( рцЫзс: чотй чтцпс() ( соцп « " Функция чтопс() из класса йет1чей1. 1п"т ) )," с1аяя йет1чей2: рцЫьс йетьчей1 рцЫьс: /* Функция чтцпс О не замещается а классе йетъчей2. Поскольку класс йеткчей2 является наследником класса йелъчей1, вызывается Функция чбцпс() из класса йетгчей1.
*/ ) 1пе яьъ1п() ( Ьаве *р, Ьг йегачей1 й1; йетлчей2 й2; // Указатель на объект класса Ьаве р = БЬ; р->чгцпс(); // Вызов Функции чбцпс() из класса Ьаяе. // Указатель на объект класса йетьчей1. р = ьй1т р->ч/цпс О г // Функция чдцпс() из класса йелтчей1.
// Указатель на объект класса йелзчей2 р = ьй2т р->чтопс О т // Функция чгппс О из класса йельчей1. пегцтп О; ) Программа выводит на зкран следующие сообщения. ФУнкция чтипс() из класса Ьаяе. Функция чгопс() из класса йетъчей1. Функция чгцпс() из класса йет1чей1. Чисто виртуальные функции Итак. если виртуальная функция не замещается в производном классе, вызывается ее версия из базового класса. Однако во многих случаях невозможно создать разумну)о версию виртуальной функции в базовом классе.
Например, базовый класс может не обладать достаточным объемом информации лля создания виртуальной функции. Кроме того, в некоторых ситуациях необходилю гарантировать, что виртуаяьная функция будет замещена во всех производных классах. Для этих ситуаций в языке С++ предусмотрены чисто виртуальные функции. Числ)о виртуальная функция (рцге Ипца! 1апс((оп) — это виртуальная функция, не имеющая определения в базовом классе. Для объявления чисто виртуальной функции используется следующая синтаксическая конструкция. и чйттца1 гпяп имя функции(список парачатраа) =- 0; Часть й. Язык С++ Чисто виртуальные функции лолжны переопредсляться в каждом производном классе, в противном случае возникнет ошибка компиляции.
Слелуюшая программа солержит простой пример чисто виртуальной функции. Базовый класс ишаев содержит целое число ча1, функцию веача1() и чисто виртуальную функцию вьаи() . Пронзволныс классы ьехеуре. йесеуре и асееуре являются наследниками класса пвхкЬее и переоцредсляют функцию вцоы() так, что она выводит значение ча1 в соответствуюшей системе счисления (шестнвдцатернЧной, десятичной или восьмеричной). рвпс1цйе «хавеееаи> ия?пд памеярасе яей; с1авя пшлЬек ( рхоеесеей: 1пе ча1; рцЫ ).с: чохи яехча1(хпс х) ( ча1 = // Функция яцоы(! является чисто виртуальной. чхтеца1 чохй вцон() = 0; ): с1аяя Ьехеуре : риЬ1хс пцпхЬех рцЬ1?с: чохи яцаи() ( аоце « Ьех « ча) « "1п"; ) ); с1аяя йесеуре ." риЬ1хс пшпЬех рцЫхс: чо1й вцоы() ( соие «< ча1 « "М" ? ) с1авв асееуре: риЬ?хс пцтЬех ( роЫ 1с: чохй яиоы() ( соие « осе « ча1 « "чп"; апе вахп() ( йесеуре й; Ьехеуре Ь," осееуре о; й.ве?ча1(20); й.яцоы()? // Выводит десятичное число 20.
Ь.яееча?(20); Ь.яцоы(); // Выводит иестнадцатеричное число 14. о.яееча1(20); о.яцои(); // Выводит восьмеричное число 24. хаги.п 0; Глава 17. Виртуальные функции и полиморфизм Несмотря иа простоту этого примера, ои достаточно ярко иллюстрирует ситуацию, котла в базовом классе невозможно лать осмысленное опрсделсиие виртуальной функции. В деппом случае класс пцж)зее просто обеспечивает единообразный интерфейс для использования произволиых типов. Функцию в?юм() невозможно определить в классе пяжъее, поскольку в исм це задана основа системы счисления. Однако чисто виртуальная функция вЬомО гарантирует, что в каждом производиом классе опа будет соответствующим образом переопределена.
Следует иметь в виду, что всс производные классы обязаны переопределять чисто виртуальную функцию. Если этого цс слслать. возпикист ошибка компиляции, ~~® Абстрактные классы Класс, содержащий хотя бы одну чисто виртуальную функцию, называется абпядакглиым (аЬа(гас( с!азз). Поскольку абстрактный класс солсржит одну или несколько фупкций, ие имеющих опрелелсиия (т.е, чисто виртуальэыс функции), его объекты создать исвозлюжио. Слеловатсльпо, абстрактиые классы можно использовать лишь как основу для производных классов.
Несмотря иа то что объекты абстрактного класса ие существуют, можио создать указатели и ссылки на абстрактный класс. Это позволяет применять абстрактпые классы лля поддержки динамического полиморфизма и выбирать соответствующую виртуальную функцию в зависимости от типа указателя или ссылки. ~ ~ Применение виртуальных функций В основу объектно-ориентированного программироваиия положен принцип "олин интерфейс, несколько методов". Оц позволяет определять базовый класс операций с елииообразэыл1 и~тсрфсисом, а их конкретизацию прслоставлять производным классам. Виртуаль~ыс функции, абстрактные классы и динамический полиморфизм представляют собой опии из наиболее мощных и гибких механизмов реализации принципа "один иитсрфсис, несколько метолов".
Используя этот механизм, можно создавать иерархии классов, оргапизоваипые по принципу "от общего — к частному" (от базового класса — к производным). По этому методу в базовом классе следует определять все упиверсальиыс свойства и интерфейсы. Если некоторую операцию можно реализовать только в произволпом классе, используется виртуальная функция. По существу, в базовом классе описываются лишь самые общие свойства, а в производных классах опи конкретизируются.
Проиллюстрируем сказанное следующим примером, в котором создается иерархия классов, выполняющих преобразование единицы измерения из одной системы мер в другую (папример, литров — в галлоны). В базовом классе еоизгеее объявлены две псремепиые — зга11 и та12, в которых хранятся исходиое и преобразованное значения соответствеиио. Кроме того, в ием определены функции яее1п1еО и яеееопчО, возвращающие эти значения.