Простое приложение виртуальных функций



 

Чтобы вы могли получить представление о силе принципа "один интерфейс, множество методов", рассмотрим следующую короткую программу. Она создает базовый класс figure , предназначенный для хранения размеров различных двумерных объектов и вычисления их площадей. Функция set_dim() является стандартной функцией‑членом, поскольку эта операция подходит для всех производных классов. Однако функция show_area() объявлена как виртуальная, так как методы вычисления площади различных объектов будут разными. Программа использует базовый класс figure для выведения двух специальных классов rectangle и triangle .

 

 

Вот как выглядят результаты выполнения этой программы.

 

 

В этой программе обратите внимание на то, что при работе с классами rectangle и triangle используется одинаковый интерфейс, несмотря на то, что в них реализованы собственные методы вычисления площади соответствующих объектов.

Как вы думаете, используя объявление класса figure , можно вывести класс circle для вычисления площади круга по заданному значению радиуса? Ответ: да. Для этого достаточно создать новый производный тип, который бы вычислял площадь круга. Могущество виртуальных функций опирается на тот факт, что программист может легко вывести новый тип, который будет разделять общий интерфейс с другими "родственными" объектами. Вот, например, как это можно сделать в нашем случае:

 

 

Прежде чем опробовать класс circle в "деле" , рассмотрим внимательно определение функции show_area() . Обратите внимание на то, что в нем используется только одно значение переменной x , которая должна содержать радиус круга. (Вспомните, что площадь круга вычисляется по формуле пR2 .) Однако согласно определению функции set_dim() в классе figure ей передается два значения, а не одно. Поскольку классу circle не нужно второе значение, то что мы можем предпринять?

Есть два способа решить эту проблему. Первый (и одновременно наихудшим) состоит в том, что мы могли бы, работая с объектом класса circle , просто вызывать функцию set_dim() , передавая ей в качестве второго параметра фиктивное значение. Основной недостаток этого метода – отсутствие четкости в задании параметров и необходимость помнить о специальном исключении, которое нарушает действие принципа: "один интерфейс, множество методов" .

Есть более удачный способ решения этой проблемы, который заключается в предоставлении параметру функции set_dim() значения, действующего по умолчанию. В этом случае при вызове функции set_dim() для круга нужно задавать только радиус. При вызове же функции set_dim() для треугольника или прямоугольника задаются оба значения. Ниже показана программа, в которой реализован этот метод.

 

 

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

 

 

 Несмотря на то что виртуальные функции синтаксически просты для понимания, их настоящую силу невозможно продемонстрировать на коротких примерах. Как правило, могущество полиморфизма проявляется в больших сложных системах. По мере освоения C++ вам еще не раз представится случай убедиться в их полезности.

 

Чисто виртуальные функции и абстрактные классы

 

Как вы могли убедиться, если виртуальная функция, которая не переопределена в производном классе, вызывается объектом этого производного класса, то используется версия, определенная в базовом классе. Но во многих случаях вообще нет смысла давать определение виртуальной функции в базовом классе. Например, в базовом классе figure (из предыдущего примера) определение функции show_area() – это просто заглушка. Она не вычисляет и не отображает площадь ни одного из объектов. Как вы увидите при создании собственных библиотек классов, в том, что виртуальная функция не имеет значащего определения в контексте базового класса, нет ничего необычного.

Чисто виртуальная функция – это виртуальная функция, которая не имеет определения в базовом классе.

Существует два способа обработки таких ситуаций. Первый (он показан в предыдущем примере программы) заключается в обеспечении функцией вывода предупреждающего сообщения. Возможно, такой подход и будет полезен в определенных ситуациях, но в большинстве случаев он попросту неприемлем. Например, можно представить себе виртуальные функции, без определения которых в существовании производного класса вообще нет никакого смысла. Рассмотрим класс triangle . Он абсолютно бесполезен, если в нем не определить функцию show_area() . В этом случае имеет смысл создать метод, который бы гарантировал, что производный класс действительно содержит все необходимые функции. В C++ для решения этой проблемы и предусмотрены чисто виртуальные функции.

Чисто виртуальная функция – это функция, объявленная в базовом классе, но не имеющая в нем никакого определения. Поэтому любой производный тип должен определить собственную версию этой функции, ведь у него просто нет никакой возможности использовать версию из базового класса (по причине ее отсутствия). Чтобы объявить чисто виртуальную функцию, используйте следующий общий формат:

 

 

Здесь под элементом тип подразумевается тип значения, возвращаемого функцией, а элемент имя_функции – ее имя. Обозначение = 0 является признаком того, что функция здесь объявляется как чисто виртуальная. Например, в следующей версии определения класса figure функция show_area() уже представлена как чисто виртуальная.

 

 

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

 

 

Класс, который содержит хотя бы одну чисто виртуальную функцию, называется абстрактным.

Если класс имеет хотя бы одну чисто виртуальную функцию, его называют абстрактным . Абстрактный класс характеризуется одной важной особенностью: у такого класса не может быть объектов. Абстрактный класс можно использовать только в качестве базового, из которого будут выводиться другие классы. Причина того, что абстрактный класс нельзя использовать для создания объектов, лежит, безусловно, в том, что его одна или несколько функций не имеют определения. Но даже если базовый класс является абстрактным, его все равно можно использовать для объявления указателей и ссылок, которые необходимы для поддержки динамического полиморфизма.

 


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

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






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