print_total(cout, derived, 10); // вызов Bulk_quote::net_price()

В первом вызове параметр item связан с объектом типа Quote. В результате, когда функция print_total() вызовет функцию net_price(), выполнится ее версия, определенная в классе Quote. Во втором вызове параметр item связан с объектом класса Bulk_quote. В этом вызове функция print_total() вызывает версию функции net_price() класса Bulk_quote.

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

base = derived;     // копирует часть Quote производного в базовый

base.net_price(20); // вызов Quote::net_price()

Когда происходит вызов виртуальной функции в выражении с обычным типом (не ссылкой и не указателем), такой вызов привязывается во время компиляции. Например, когда происходит вызов функции net_price() объекта base, нет никаких вопросов о выполняемой версии. Можно изменить значение (т.е. содержимое) объекта, который представляет base, но нет никакого способа изменить тип этого объекта. Следовательно, этот вызов распознается во время компиляции как версия Quote::net_price().

Ключевая концепция. Полиморфизм в языке С++

Одной из ключевых концепций ООП является полиморфизм (polymorphism). В переводе с греческого языка "полиморфизм" означает множество форм. Связанные наследованием типы считаются полиморфными, поскольку вполне можно использовать многообразие форм этих типов, игнорируя различия между ними. Краеугольным камнем поддержки полиморфизма в языке С++ является тот факт, что статические и динамические типы ссылок и указателей могут отличаться.

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

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

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

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

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

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

Перейти на страницу:

Похожие книги