- •Лабораторная работа № 5. Большое Домашнее Задание (бдз) Наследование классов, виртуальные функции
- •Теоретические сведения
- •Передача параметров в базовый класс
- •Виртуальные функции и полиморфизм
- •Ограничения на использование виртуальных функций
- •Контрольные вопросы
- •Общие требования выполнения работы
- •Обязательные функции для всех вариантов:
Виртуальные функции и полиморфизм
Механизм виртуальных функций в ООП используется для создания метода, предназначенного для работы с различными (по типам) объектами. Например методы ввода-вывода необходимы для всех объектов производных классов, поэтому целесообразно их определять в базовом классе, а затем переопределять в производных классах.
Виртуальные функции объявляются в базовом классе с ключевым словом virtual. Механизм позднего связывания действует автоматически, программисту достаточно объявить функцию в базовом классе как virtual и переопределить ее в производных классах.
В отличие от перегружаемых функций виртуальные объявляются в производных классах с той же сигнатурой, что и в базовом классе (имя функции, возвращаемое значение, количество и типы аргументов). Если сигнатуры функций базового и производных классов различны, то включается механизм перегрузки, а не виртуальных функций.
Итак, для включения механизма позднего (динамического) связывания необходимо выполнение следующих условий:
в базовом классе определяется прототип функции и объявляется как virtual
в производных классах функция переопределяется
обращение к функции выполняется через указатель базового класса
Пример 5: Объявим в базовом классе виртуальную функцию show ()
class point
{. . .
virtual void show ()
{ cout<<x<<'\t'<<y<<endl;}
};
В производных классах circ и сylinder определение функций остается без изменений. Обращаем внимание на то, что указатель базового класса может адресовать объекты как базового, так и производного класса.
int _tmain(int argc, _TCHAR* argv[])
{point p1(1,2),*pp;
cout<<"-------p1-----"<<endl;
pp=&p1; // Указатель базового класса связываем с точкой
pp->show(); // печать информации о точке
circ s1(1,2,3);
cout<<"-------s1-----"<<endl;
pp=&s1; // Указатель базового класса связываем с окружностью
pp->show(); // печать информации об окружности
cylinder c2(0,10,15,50);
pp=&c1; // Указатель базового класса связываем с цилиндром
pp->show(); // печать информации о цилиндре
system("pause");
return 0;
}
Как легко заметить, в отличие от предыдущего примера 4 ,где функцию вызывал объект класса ( вызов p1.show() и т.п.), в примере 5 по внешнему виду невозможно определить функция какого класса будет вызываться (вызов pp->show() не несет этой информации), поэтому как всегда при динамической работе, все определяет контекст работы программы.
Ограничения на использование виртуальных функций
Переопределяемая функция в производных классах должна соответствовать прототипу виртуальной функции базового класса. То есть при переопределении виртуальной функции интерфейс функции должен в точности соответствовать прототипу. Если же такого соответствия нет, то такая функция просто рассматривается как перегруженная и она утрачивает свои виртуальные свойства.
Виртуальная функция должна быть членом, а не другом класса, для которого она определена. Тем не менее виртуальная функция может быть другом другого класса.
Хотя деструктор может быть виртуальным, но конструктор виртуальным быть не может.
Если в производном классе виртуальная функция не переопределяется, то тогда используется ее версия из базового класса. Например, если закомментировать функцию show() в классе cylinder, то увидим: circle : x=2 y=3 r=6 p=37.68 q=113.04
int main(int argc, char* argv[])
{ . . .
cylinder c1,c2(0,10,15,50),c3(s2,9);
pp= &c3; //указатель связывается с объектом класса Cylinder
pp->show(); // вызывается show() класса circ
}
В общем случае, когда класс не переопределяет виртуальную функцию, С++ использует первое из определений, которое он находит, идя от потомков к предкам.
