Наследование виртуальных функций



 

Атрибут virtual передается "по наследству".

Если функция объявляется как виртуальная, она остается такой независимо от того, через сколько уровней производных классов она может пройти. Например, если бы класс second_d был выведен из класса first_d , а не из класса base , как показано в следующем примере, то функция who() по‑прежнему оставалась бы виртуальной, и механизм выбора соответствующей версии по‑прежнему работал бы корректно.

 

 

Если производный класс не переопределяет виртуальную функцию, то используется функция, определенная в базовом классе. Например, проверим, как поведет себя версия предыдущей программы, если в классе second_d не будет переопределена функция who() .

 

 

Теперь при выполнении этой программы на экран выводится следующее.

 

 

Как подтверждают результаты выполнения этой программы, поскольку функция who() не переопределена классом second_d , то при ее вызове с помощью инструкции p‑>who() (когда член р указывает на объект second_obj ) выполняется та версия функции who() , которая определена в классе base .

Следует иметь в виду, что наследуемые свойства спецификатора virtual являются иерархическими. Поэтому, если предыдущий пример изменить так, чтобы класс second_d был выведен из класса first_d , а не из класса base , то при обращении к функции who() через объект типа second_d будет вызвана та ее версия, которая объявлена в классе first_d , поскольку этот класс является "ближайшим" (по иерархическим "меркам") к классу second_d , а не функция who() из тела класса base . Эти иерархические зависимости демонстрируются на примере следующей программы.

 

 

Эта программа генерирует такие результаты.

 

 

Как видите, класс second_d теперь использует версию функции who() , которая определена в классе first_d , поскольку она ближе всех в иерархической цепочке классов.

 

Зачем нужны виртуальные функции

 

Как отмечалось в начале этой главы, виртуальные функции в сочетании с производными типами позволяют C++ поддерживать динамический полиморфизм. Полиморфизм существенен для объектно‑ориентированного программирования по одной важной причине: он обеспечивает возможность некоторому обобщенному классу определять функции, которые будут использовать все производные от него классы, причем производный класс может определить собственную реализацию некоторых или всех этих функций. Иногда эта идея выражается следующим образом: базовый класс диктует общий интерфейс, который будет иметь любой объект, выведенный из этого класса, но позволяет при этом производному классу определить метод, используемый для реализации этого интерфейса. Вот почему для описания полиморфизма часто используется фраза "один интерфейс, множество методов" .

Для успешного применения полиморфизма необходимо понимать, что базовый и производный классы образуют иерархию, развитие которой направлено от большей к меньшей степени обобщения (т.е. от базового класса к производному). При корректной разработке базовый класс обеспечивает все элементы, которые производный класс может использовать напрямую. Он также определяет функции, которые производный класс должен реализовать самостоятельно. Это дает производному классу гибкость в определении собственных методов, но в то же время обязывает использовать общий интерфейс. Другими словами, поскольку формат интерфейса определяется базовым классом, любой производный класс должен разделять этот общий интерфейс. Таким образом, использование виртуальных функций позволяет базовому классу определять обобщенный интерфейс, который будет использован всеми производными классами.

Теперь у вас может возникнуть вопрос: почему же так важен общий интерфейс со множеством реализаций? Ответ снова возвращает нас к основной побудительной причине возникновения объектно‑ориентированного программирования: такой интерфейс позволяет программисту справляться со все возрастающей сложностью программ. Например, если корректно разработать программу, то можно быть уверенным в том, что ко всем объектам, выведенным из базового класса, можно будет получить доступ единым (общим для всех) способом, несмотря на то, что конкретные действия у одного производного класса могут отличаться от действий у другого. Это означает, что программисту придется помнить только один интерфейс, а не великое их множество. Кроме того, производный класс волен использовать любые или все функции, предоставленные базовым классом. Другими словами, разработчику производного класса не нужно заново изобретать элементы, уже имеющиеся в базовом классе. Более того, отделение интерфейса от реализации позволяет создавать библиотеки классов, написанием которых могут заниматься сторонние организации. Корректно реализованные библиотеки должны предоставлять общий интерфейс, который программист может использовать для выведения классов в соответствии со своими конкретными потребностями. Например, как библиотека базовых классов Microsoft (Microsoft Foundation Classes – MFC ), так и более новая библиотека классов .NET Framework Windows Forms поддерживают Windows‑программирование. Использование этих классов позволяет писать программы, которые могут унаследовать множество функций, нужных любой Windows‑программе. Вам понадобится лишь добавить в нее средства, уникальные для вашего приложения. Это – большое подспорье при программировании сложных систем.

 


Дата добавления: 2018-09-22; просмотров: 333; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!