Виртуальной называется функция, которая может использоваться даже не будучи каким-либо образом определенной. C помощью виртуальных функций объект сам определяет свое поведение (собственные действия). Технику использования виртуальных функций называют полиморфизмом. Под полиморфизмом понимается возможность ассоциирования нескольких задач с одним именем функции средствами позднего связывания. Поэтому полиморфизм, позднее связывание и виртуальные функции имеют тесную связь. Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций». Полиморфизм реализуется с помощью наследования классов и виртуальных функций.
Определяя функцию как виртуальную, мы, тем самым, как бы говорим компилятору: «Я не знаю, как эта функция реализована. Подожди, пока её нужно будет использовать в программе, и тогда возьми реализацию из экземпляра объекта». Эта технология, когда определение реализации процедуры откладывается до времени выполнения программы, называется поздним или динамическим связыванием. Виртуальные функции являются средством позднего связывания, предоставляемого программистам языком C++.
К механизму виртуальных функций обращаются в тех случаях, когда в каждом производном классе требуется иметь свой вариант некоторой компонентной функции. Любая нестатическая функция базового класса может быть сделана виртуальной, для чего используется ключевое слово virtual.
Для иллюстрации применения виртуальных методов приведем следующий пример.
Пример 7
#include <iostream.h>
#include <windows.h>
class vehicle // класс "транспортное средство"
{
int wheels;
float weight;
public:
virtual void message(void) {cout << "Транспортное средствоn";}
// описание виртуальной функции message класса vehicle и реализация этой
// функции. При вызове функции message класса vehicle на экран монитора
// будет выведена строка "Транспортное средство"
};
class car : public vehicle // класс "легковая машина", унаследованный из
// класса "транспортное средство"
{
int passenger_load;
public:
void message(void) {cout << "Легковая машинаn";}
};
class truck : public vehicle // класс "грузовая машина", унаследованный из
// класса "транспортное средство"
{
int passenger_load;
float payload;
public:
int passengers(void) {return passenger_load;}
};
class boat : public vehicle // класс "лодка", унаследованный из класса
// "транспортное средство"
{
int passenger_load;
public:
int passengers(void) {return passenger_load;}
void message(void) {cout << "Лодкаn";}
};
void main()
{
::SetConsoleCP(::GetACP());
::SetConsoleOutputCP(::GetACP());
vehicle *unicycle;
unicycle = new vehicle;
unicycle->message(); // вызываем метод message объекта
delete unicycle;
unicycle = new car;
unicycle->message();
delete unicycle;
unicycle = new truck;
unicycle->message();
delete unicycle;
unicycle = new boat;
unicycle->message();
delete unicycle;
}
Результаты работы программы представлены на рис. 14.3.
Рис. 14.3. Результат работы программы (пример 7)
Таким образом, интерпретация каждого вызова виртуальной функции через указатель на базовый класс зависит от значения этого указателя, т.е. от типа объекта, для которого выполняется вызов. Выбор того, какую виртуальную функцию вызвать, будет зависеть от типа объекта, на который фактически (в момент выполнения программы) направлен указатель, а не от типа указателя. Виртуальными могут быть только нестатические функции-члены.Виртуальность наследуется. После того как функция определена как виртуальная, ее повторное определение в производном классе (с тем же самым прототипом) создает в этом классе новую виртуальную функцию, причем спецификатор virtual может не использоваться.
Виртуальная функция может объявляться с параметрами, она может возвращать значение, как и любая другая функция. В классе может объявляться столько виртуальных функций, сколько необходимо. Они могут находиться в любой части класса – закрытой, открытой или защищенной.