|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Виртуальные функции
Мы уже упоминали о полиморфизме - важной особенности объектно- ориентированного программирования. Рассмотрим следующий пример (6):
#include <iostream.h> class X { public: double A(double x) { return x * x; } double B(double x) { return A(x) / 2; } }; class Y: public X { public: double A(double x) { return x * x * x; } }; int main () { Y y; cout << y.B(3) << endl; return 0; }
В классе X объявляются функции A и B, причем функция B вызывает функцию А. Класс Y, потомок класса X, наследует функцию B, но переопределяет функцию A. Цель этого примера - демонстрация полиморфного поведения класса Y. Мы должны получить следующий результат: вызов наследуемой функции X::B должен привести к вызову функции Y::A. Что же выдаст нам наша программа? Ответом будет 4.5, а не 13.5! В чем же дело? Почему компилятор разрешил выражение y.B(3) как вызов наследуемой функции X::B, которая, в свою очередь, вызывает X::A, а не функцию Y::A, что должно было бы произойти в случае полиморфной реакции класса? Виртуальные функции объявляются следующим образом (синтаксис):
class className1 { // функции-элементы virtual returnType functionName(<список параметров>); }; class className2: public className1 { // функции-элементы virtual returnType functionName(<список параметров>); };
Пример 7, показывающий, как при помощи виртуальных функций можно реализовать полиморфное поведение классов X и Y:
#include <iostream.h> class X { public: virtual double A(double x) { return x * x; } double B (double x) { return A(x) / 2; } }; class Y: public X { public: virtual double A(double x) { return x * x * x; } }; main() { Y y; cout << y.B(3) << endl; return 0; }
Этот пример выведет вам правильное значение 13.5, потому что в результате вызова наследуемой функции X::B, вызывающей функцию A, в качестве функции A во время выполнения программы будет использована замещающая функция Y::A.
*** Правило виртуальной функции ***
Правило виртуальной функции гласит:
"Виртуальная однажды - виртуальна всегда".
Это означает следующее. Если вы объявили функцию как виртуальную в некотором классе, то в классах-потомках, переопределяющих эту функцию, она также будет виртуальной, но только если она имеет тот же список параметров. Если переопределенная функция в классе-потомке имеет другой список параметров, то ее версия из базового класса будет недоступна классу-потомку (и всем его потомкам). Это может показаться неудобным, но только на первый взгляд. Правило это справедливо и для всех языков объектно-ориентированного программирования, поддерживающих виртуальные функции, но не допускающих перегрузку функций. В С++ положение несколько иное. Вы можете объявлять невиртуальные перегруженные функции, совпадающие по имени с виртуальными функциями, но имеющие другой список параметров. И, кроме того, вы не можете наследовать невиртуальные функции, имя которых совпадает с виртуальными функциями. Рассмотрим пример 8, иллюстрирующий сказанное.
#include <iostream.h> class A { public: A() {} virtual void foo(char c) { cout << "virtual A::foo() returns " << c << endl; } }; class B: public A { public: B() {} void foo(const char* s) { cout << "B::foo() returns " << s << endl; } void foo(int i) { cout << "B::foo() retuzns " << i << endl; } virtual void foo(char c) { cout << "virtual B::foo() returns " << c << endl; } }; class C: public B { public: C() {} void foo(const char* s) { cout << "C::foo() returns " << s << endl; } void foo(double x) { cout << "C::foo() returns " << x << endl; } virtual void foo(char c) { cout << "virtual C::foo() returns " << c << endl; } }; int main() { A Aobj; B Bobj; C Cobj; Aobj.foo('A'); Bobj.foo('B'); Bobj.foo(10); Bobj.foo("Bobj"); Cobj.foo('C'); Cobj.foo(144.123); Cobj.foo("Cobj"); return 0; }
В этом примере вводятся три класса - A, B и C - образующих линейную иерархию наследования. В классе A объявляется виртуальная функция foo(char). Класс B объявляет свою версию виртуальной функции foo(char), но, кроме того, в классе B объявляются невиртуальные перегруженные функции foo(const char*) и foo(int). Класс C объявляет свою версию виртуальной функции foo(char) и невиртуальные перегруженные функции foo(const char*) и foo(double). Обратите внимание на то, что в классе C приходится заново объявлять функцию foo(const char*), поскольку в данном случае функция-элемент B::foo(const char*) не наследуется. Таким образом, в С++ схема наследования отличается от обычной для случая виртуальной и перегруженных функций с одинаковым именем. В функции main объявляются объекты для всех трех классов и вызываются различные версии функции-элемента foo.
Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.005 сек.) |