Виртуальные функции – это функции-члены класса, вызов которых осуществляется во время выполнения программы ( то есть динамически) по типу объекта, с которым она используется.
Например, базовый класс может описывать фигуру на экране без конкретизации ее вида, а производные классы (окружность, эллипс и т.п.) однозначно определяют ее формы и размеры. Если в базовом классе ввести функцию для изображения фигуры на экране, то выполнение этой функции будет возможно только для объектов каждого из производных классов, определяющих конкретные изображения.
Объявление виртуальной функции в классе:
virtual прототип_функции;
class Point: public Location{
protected:
Boolean vis;
public:
Point (int nx,int
ny);
virtual void
Show();
virtual void
Hide();
virtual void
Drag(int by);
Boolean
Isvis() { return vis;}
void MoveTo
(int nx ,int ny); };
Виртуальными могут быть не любые функции, а только нестатические
компонентные функции какого-либо класса. После того как функция определена как
виртуальная, ее повторное определение в производном классе (с тем же самым
прототипом) создает в этом классе новую виртуальную функцию, причем
спецификатор virtual может не использоваться.
В производном классе
нельзя определять функцию с тем же именем и с той же сигнатурой параметров, но
с другим типом возвращаемого значения, чем у виртуальной функции базового
класса. Это приводит к ошибке на этапе компиляции.
Если в производном
классе ввести функцию с тем же именем и типом возвращаемого значения, что и
виртуальная функция базового класса, но с другой сигнатурой параметров, то эта
функция производного класса не будет виртуальной. В этом случае с помощью
указателя на базовый класс при любом значении этого указателя выполняется
обращение к функции базового класса (несмотря на спецификатор virtual и присутствие в производном классе похожей функции).
Рассмотрим как будет
выглядеть вызов виртуальной функции void Show() в производном классе:
class Circle : public Point {
protected:
int R;
public:
Circle (int
nx,int ny, int nr);
void Show();
void Hide();
void
Expand(int by);
void Contract(int by); };
Как мы видим,
спецификатор virtual можно уже не указывать.
Механизм виртуального
вызова может быть подавлен с помощью явного использования полного
квалифицированного имени. Таким образом, при необходимости вызова из
производного класса виртуального метода (компонентной функции) базового класса
употребляется полное имя.
Все выше сказанное
можно объединить в ПРАВИЛА:
1)не объявлять static;
2)Объявления виртуальных функций в
производных классах должны иметь ту же сигнатуру, что и в базовом. Указывать
спецификатор virtual в этом случае не обязательно.
3)Виртуальная функция должна быть
обязательно определена или быть чистой виртуальной функцией.
Чистой
виртуальной функцией
называется компонентная функция, которая имеет следующее определение:
virtual
тип имя_функции (список
формальных параметров)=0;
В этой записи конструкция “=0” называется “чистый спецификатор”.
Пример описания чистой виртуальной функции:
Чистая
виртуальная функция в определении класса показывает, что ее определение
откладывается до производных классов. Чистая виртуальная функция “ничего не
делает” и недоступна для вызовов. Ее назначение – служить основой для
подменяющих ее функций в производных классах и показывать, что данный класс
абстрактный. Виртуальные классы
При таком наследовании может возникнуть проблема в наследовании двух экземпляров полей класса A в классе D через B и C (будет занята лишняя память и возникнет путаница). Чтобы это избежать, нужно при создании B и C объявить класс виртуальным.
class B : virtual public A
{…}
class C : virtual public A
{…}
class D : public C, public B
В этом случае последовательность в создании объектов класса D будет следующей: сначала вызывается конструктор класса А (вызывается один раз), затем конструкторы В и С, последним вызывается конструктор класса D. Уничтожение объектов класса D производится в обратном порядке.