Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Глава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; Нарушение авторского права страницы