Добавил:
владимир Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_С++ / Лабы / ООП_Лабораторная работа №4.doc
Скачиваний:
0
Добавлен:
17.08.2025
Размер:
207.36 Кб
Скачать

Виртуальные функции и полиморфизм

Механизм виртуальных функций в ООП используется для создания метода, предназначенного для работы с различными (по типам) объектами. Виртуальные функции объявляются в базовом и производных классах с ключевым словом virtual. Механизм позднего связывания действует автоматически, программисту достаточно объявить функцию в базовом классе как virtual и переопределить ее в производных классах.

Но неплохо понимать этот автоматический механизм: каждый объект класса, управляемого из базового класса с виртуальными функциями, имеет указатель на таблицу с именем vmtbl (virtual method table), содержащую адреса виртуальных функций. Эти 35 адреса устанавливаются в адреса нужных для данного объекта функций во время выполнения.

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

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

Объявим в базовом классе виртуальную функцию 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;

}

Как легко заметить, в отличие от предыдущего примера ,где объект класса вызывал функцию( вызов p1.show() и т.п.), в данном примере по внешнему виду невозможно определить функция какого класса будет вызываться (вызов pp->show() не несет этой информации), поэтому как всегда при динамической работе, все определяет контекст работы программы, и этот контекст может динамически изменяться.

Ограничения на использование виртуальных функций:

  1. Переопределяемая функция в производных классах должна соответствовать прототипу виртуальной функции базового класса. То есть при переопределе­нии виртуальной функции интерфейс функции должен в точности соответствовать прототипу. Если же такого соответствия нет, то такая функция просто рассматривается как перегруженная и она утрачивает свои виртуальные свойства.

  2. Вирту­альная функция должна быть членом, а не другом класса, для которого она определена. Тем не менее виртуальная функция может быть другом другого класса.

  3. Хотя деструктор может быть виртуальным, но конструктор виртуальным быть не может.

Если в производном классе виртуальная функция не переопределяется, то тогда используется ее версия из базового класса. Например, если закомментировать функцию 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

}

В общем случае, когда класс не переопределяет виртуальную функцию, С++ использует первое из определений, которое он находит, идя от потомков к предкам.