Перегрузка операций в С++
Информация - Компьютеры, программирование
Другие материалы по предмету Компьютеры, программирование
?обность операции "+"main( )
{A("111");.Print( );B("222");.Print( );C(" ");= A + B;.Print( );
}
4. Полиморфизм
В одном и том же классе можно определить или описать любое число методов с одинаковым именем. При этом важно лишь то, чтобы формальные параметры одноименных функций-методов как-нибудь различались. Этот случай обычно называют перегрузкой меетодов-функций.
Однако и подкласс (производный класс) может содержать функцию-метод с именем, совпадающим с уже присутствующим в базовом классе. Такой случай назвают полиморфизмом. Например, конструкции типа SubClass из нижеследующего фрагмента программы могут иметь собственные методы для печати print( ).
SubClass : public Base {(char* n) : Base(n) { }print( ) {<< "Это компонент подкласса="
<< name
<< endl;
}
};main( ) {aBaseObject;aSubClassObject;
}
Ссылка на print( ) по умолчанию относится к методу самого низкого уровня, который применим в этом случае. В приведенном примере aBaseObject.print( ) ссылается на Base::print( ), тогда как aSubClassObject.print( ) ссылается на SubClass::print( ). Программа может вызывать конкретную функцию, если задано полностью квалифицированное имя, например aSubClassObject.Base::print( ). Методы подкласса могут таким же способом ссылаться на методы базового класса. Вместе с тем программа не может ссылаться на aBaseObject.SubClass:: print( ), поскольку SubClass::print( ) не является компонентом класса Base.
Если метод подкласса перекрывает своим новым определением метод базового класса, то могут возникнть проблемы. Рассмотрим, что будет означать для функции fn( ) определение нового метода print( ).
fn(Base& aBaseRef) {<<"Из fn( ):";.print( );
}
Объект aBaseRef теперь объявлен как относящийся к классу Base. Ссылка на aBaseRef.print() всегда будет фактически относиться к Base::print(), но вместо объекта базового класса будет использован объект подкласса. Таким образом, fn() можно вызывать либо как fn(aSub ClassObject), либо как fn(aBaseClassObject). Вызов print() соответственно, приведет к вызову функции SubClass:: print().
Как ужк было сказано ранее, использование одного и того же имени при вызове функции для разных типов параметров или для классов разных уровней наследования называется полиморфизмом. Для поддержания полиморфизма язык программирования должен иметь возможность во время выполнения принимать решение о том, какая именно компонентная функция с этим именем должна быть вызвана. Этот процесс носит название позднего связывания. Такое название этого механизма говорит о том, что выбор полиморфной функции-метода, которую необходимо выполнить в данном месте программы, осуществляется непосредственно в процессе выполнения программы. Вследствие сказанного, позднее связывание замедляет выполнение программы, т.к. при вызове полиморфной функции программа обязана выполнить ряд лишних обращений к оперативной памяти компьютера.++ оставляет использование позднего связывания на усмотрение программиста. По умолчанию, даже при наличии неоднозначности имен, транслятором принимается решение о раннем связывании имен программы. Следовательно, по способу записи определений классов fn( ) всегда будет давать обращение к Base::print( ). Позднее связывание здесь не выполняется. Добавление в определение функции ключевого слова virtual делает эту функцию полиморфной.
void print( ) {<< "Это компонент базового класса="
<< name
<< endl;
}
Вызовы virtual print( ) используют позднее связывание.
Виртуальная функция не может быть объявлена как static (статическая). При отсутствии объекта Turbo C++ не может выполнить позднее связывание.
Объявление виртуальной функции-метода автоматически делает виртуальными все функции с этим именем в подклассах.
Если метод в подклассе с тем же именем принимает другие аргументы, то никакого полиморфизма нет. Рассмотрим пример:
#include Base {void print( ) {<< "Это объект базового класса"
<< endl;
}
};SubClass : public Base {void print(char* c) {<< "Это объект подкласса "
<< c
<< endl;
}
};fn(Base& obj) {.print( );.print("Relative object"); //ошибка компилятора #1
}main( ) {aSubClass;.print( ); //ошибка компилятора #2.print("aSubClass");(aSubClass);
}
Оба класса, Base и SubClass, содержат функции print( ); однако, эти две функции имеют разные аргументы. Компилятор C++ не позволит сделать вызов Base::print( ) с неверными типами аргументов, что приведет к ошибке, с соответствующим сообщением от компилятора. Аналогичная ситуация возникнет и во втором случае, когда компилятор встретит вызов SubClass:: print( ).
5. Правило isA( )
Если способы обработки объекта подкласса отличаются от способов обработки объектов базового класса, то предпочтительным является способ перегрузки метода объекта базового класса с новым определением. Это может в некоторых случаях оказаться неудобным, особенно если функция была реализована как некомпонентная. В таких случаях для функции необходимо знать тип объекта, с которым она имеет дело.
Для решения этой проблемы программист должен определить метод идентификации, обычно называемый isA( ). Это виртуальное правило возвращает константу, которая является уникальной для каждого типа подкласса. Можно перевести слово isA как являющийся (под)классом определенного типа. Рассмотрим следующую некомпонентную версию функции print( ).
#include Base {ClassType {BASE, SUBCLASS};ClassType isA( ) {return BASE;}
}print(Base& obj) {(obj.isA( )==Base::BASE)<< "Это объект базового класса\n";(obj.isA( )==Base::SUBCLASS)<< "Это объект подкласса\n";<< "Это неизвестный тип объекта\n";
}fn(Base& obj) {(obj) ;
}main( ) {aBaseClass;aSubClass;(aBaseClas