Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Глава22.Знакомствосвиртуальнымифункциями-членами:настоящиел




int  (int

{

s; GraduateStudent

//вызывает

();         . .

 

return 0;

}

Как и в любой ситуации  с перегрузкой,  когда     программист обращается

к calcTuitio n (), C++ должен решить, какая именно  функция                                      () вызывается. Если две функции отличаются типами аргументов, то нет никаких проблем. Даже если аргументы одинаковы, различий в именах класса достаточно, чтобы решить, какой именно вызов нужно осушествить, а значит, в этом примере нет ничего необычного. Вызов     обращается  К                                                       поскольку s локально объявлена как Student , тогда  как calcTuitio n () обращается к                                                                     { ).

Но что, если класс объекта не может быть точно определен на этапе компиляции?

Чтобы продемонстрировать подобную ситуацию, нужно просто немного приведенную выше программу:

class Student

!

//то же, что и раньше

 

{

return 0;

 

 

classGraduateStudent:publicStudent

{

float (

return0;


 

void        x)

{

 

 

}

int

{


 

//к какому из

//относится эта строка?


Student s; GraduateStudentgs; fn )

return 0;

)

На этот раз вместо прямого вызова calcTuitio n () осуществляется вызов через

промежуточную функцию fп (). Теперь все зависит от того, какой аргумент передается () , поскольку может быть как Student, так и Graduate-Student. Ведь GraduateSudent ЯВЛЯЕТСЯ Student!

 

232                 Часть IV. Наследование


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

 

Аргумент х, передаваемый (). для экономии места и времени объявлен как ссылка  на       класса Student. Если бы этот аргумент передавался по значению, C++ пришлось бы при каждом вызове () конструировать новый объект Student. В зависимости от вида класса Studer.t и количества вызовов () в итоге это может занять много времени, тогда как при вызове (Students) или fn(student* ) пере- дается только адрес. Если вы не поняли, о чем я говорю, перечитайте главу 15, "Создание указателей на объекты".

Было   бы   неплохо,    если   бы  строка         calcTuitio n {>        вызывала

: calcTuitio n () ,       когда  х является   объектом   класса

и                                                                             когда х является объектом класса GraduateStudent . Если бы C++ был настолько "сообразителен", это было бы действительно здорово! Почему? Об этом вы узнаете далее в главе.

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

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

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

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

ским корням — поли (т.е. многообразие) и морф                       дополненным широко извест- ным греко-латинским суффиксом                                                   C++ поддерживает полиморфизм. (Что и не уди- вительно. Тратил бы я столько времени на обсуждение полиморфизма, если бы он не поддерживался C++!) Чтобы подчеркнуть противоположность позднему связыванию, выбор перегружаемой функции на этапе компиляции называют ранним связыванием.

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

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

 


Поделиться:



Последнее изменение этой страницы: 2019-04-19; Просмотров: 206; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.01 с.)
Главная | Случайная страница | Обратная связь