Выбрать главу

    {

        setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */

        /* Передача функции объекта базового класса */

        Student s ;

        fn( s ) ;

        /* Передача функции объекта подкласса */

        GraduateStudent gs ;

        fn( gs ) ;

        /* Пауза для того, чтобы посмотреть на результат работы программы */

        system( "PAUSE" ) ; return 0 ;

    }

Данная программа генерирует следующий вывод:

    Функция Student::calcTuition

    Функция Student::calcTuition

    Press any key to continue...

На этот раз вместо прямого вызова calcTuition( ) осуществляется вызов через промежуточную функцию fn( ). Теперь всё зависит от того, какой аргумент передаётся fn( ), поскольку х может быть как Student, так и GraduateStudent — ведь GraduateSudent ЯВЛЯЕТСЯ Student!

«Если вы этого не знали, это вовсе не говорит о том, что вы ЯВЛЯЕТЕСЬ "чайником". Это значит, что вы не читали главу 20 , "Наследование классов"

[Помни!]

Аргумент х, передаваемый fn( ), для экономии места и времени объявлен как ссылка на объект класса Student. Если бы этот аргумент передавался по значению, С++ пришлось бы при каждом вызове fn( ) конструировать новый объект Student. В зависимости от вида класса Student и количества вызовов fn( ) в итоге это может занять много времени, тогда как при вызове fn( Student& ) или fn( Student* ) передаётся только адрес. Если вы не поняли, о чём я говорю, перечитайте главу 18, "Копирующий конструктор".

Было бы неплохо, если бы строка х.calcTuition( ) вызывала Student::calcTuition( ), когда х является объектом класса Student, и GraduateSudent::calcTuition( ), когда х является объектом класса GraduateStudent. Если бы С++ был настолько "сообразителен", это было бы действительно здорово! Почему? Об этом вы узнаете далее в главе.

Обычно компилятор уже на этапе компиляции решает, к какой именно функции обращается вызов. После того как вы щёлкаете на кнопке, которая даёт указание компилятору С++ собрать программу, компилятор должен просмотреть её и на основе используемых аргументов выбрать, какую именно перегружаемую функцию вы имели в виду.

В данном случае объявленный тип аргумента функции fn( ) не полностью описывает требования к функции. Хотя аргумент и объявлен как Student, он может оказаться также и GraduateStudent. Окончательное решение можно принять, только когда программа выполняется ( это называется "на этапе выполнения" ). И только когда функция fn( ) уже вызвана, С++ может посмотреть на тип аргумента и решить, какая именно функция-член должна вызываться: из класса Student или из GraduateStudent.

_________________

242 стр. Часть 4. Наследование

«Типы аргументов, с которыми вы сталкивались до этого времени, называются объявленными, или типами этапа компиляции. Объявленным типом аргумента х в любом случае является Student, поскольку так написано в объявлении функции fn( ). Другой, текущий, тип называется типом этапа выполнения. В случае с примером функции fn( ) типом этапа выполнения аргумента х является Student, если fn( ) вызывается с s, и GraduateStudent, когда fn( ) вызывается с gs

[Советы]

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

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