Программирование >>  Расширенная версия языка c++ 

1 ... 97 98 99 [ 100 ] 101 102 103 ... 227


Нет смысла использовать указатели на объекты базового класса так, как показано в этом примере. Однако в следующее еле вы увидите, почему для объектов производного класса столь важны указатели на объекты базового класса.


Попытайтесь запустить рассмотренную выше программу и поэкспериментируйте с ней. Например, попробуйте, объявив указатель на производный

класс, получить доступ к объекту базового класса.

10.2. Знакомство

с виртуальными функциями

Виртуальная функция (virtual function) является членом класса. Она объявляется внутри базового класса и переопределяется в производном классе. Для того, чтобы функция стала виртуальной, перед объявлением функции ставится ключевое слово virtual. Если класс, содержащий виртуальную функцию, наследуется, то в производном классе виртуальная функция переопределяется. По существу, виртуальная функция реализует идею один

интерфейс, множество методов , которая лежит в основе полиморфизма.

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

Виртуальная функция может вызываться так же, как и любая другая функция-член. Однако наиболее интересен вызов виртуальной функции через указатель, благодаря чему поддерживается динамический полиморфизм. Из предыдущего раздела вы знаете, что указатель базового класса можно использовать в качестве указателя на объект производного класса. Если указатель базового класса ссылается на объект производного класса, который содержит виртуальную функцию и для которого виртуальная функция вызывается через этот указатель, то компилятор определяет, какую версию виртуальной функции вызвать, основываясь при этом на типе объекта, на который ссылается указатель. При этом определение конкретной версии виртуальной функции имеет место не в процессе компиляции, а в процессе выполнения программы. Другими словами, тип объекта, на который ссылается указатель, и определяет ту версию виртуальной функции, которая будет выполняться. Поэтому, если два или более различных класса являются производными от содержащего виртуальную функцию, то, если

указатель базового класса ссылается на разные объекты этих производных




1. Рассмотрим короткий пример использования виртуальной функции:

Простой пример использования виртуальной функции tinclude <iostreain> using namespace std;

class base { . U .

public: . . . ,. .

int i; . , ..

base (int x) { i = x; } ,.,

virtual void funcO {

cout Выполнение функции f гшс () базового класса: ; cout 1 \n; , . .

J .

class public base (

public: , . ; v>:- С..

derivedl (int x) : base(x) ( } ,. , г-л: пч--. л1 .

voldfunc О . г-.-: . . .

cout Выполнение функции funcO класса derivedl: cout 1 * 1 \n; - ; . - -.л. ?.t>

) . . ..... ..i:- -

class derived2: public base {

public:

derived2 (intx) : base(x) { } .-

voldfunc ()

{ .,:

. cout << Выполнение функции func ( ) класса derived2 ; ;

cou i + i \n ;

) .u. . ... .

); . V. .

int mainO -

base *p;

классов, выполняются различные версии виртуальной функции. Этот процесс является реализацией принципа динамического полиморфизма. Фактически, о классе, содержащее альную функцию, говорят как о полиморфном классе (polymorphic class).



base ob (10) ; . ... - ---.Aj. ,.

derivedl d obl(10); : ./ ...

y.,. derived2 d ob2(10); . ...-. : -ч.. l, Tj

p = sob;

p->func ( ) ; функция f unc ( ) класса base

p = fid obl; .... -

p->func(); funcO производного класса derivedl

p = Sdob2;

p->func(); функция func ( ) производного класса derlved2 return 0; - --

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

Выполнение функции ( ) базового acca; 10

Выполнение функции f unc ( ) класса derivedl: 100 Вынолнение функции func() класса derlved2: 2 0

Переопределение виртуальной функции внутри производного класса может

показаться похожим на перегрузку функций. Однако эти два процесса совершенно различны. Во-первых, перегружаемая функция должна отличаться типом и/или числом параметров, а переопределяемая виртуальная функция должна иметь точно такой же тип параметров, то же их число, и такой же тип возвращаемого значения. (На самом деле, если при переопределении виртуальной функции вы изменяете число или тип параметров, она просто становится перегружаемой функцией и ее виртуальная природа теряется.) Далее, виртуальная функция должна быть членом класса. Это не относится к перегружаемым функциям. Кроме этого, если деструкторы могут быть виртуальными, то конструкторы нет. Чтобы подчеркнуть разницу между перегружаемыми функциями и переопределяемыми виртуальными функциями, для описания переопределения виртуальной функции используется термин подмена (overriding).

В рассмотренном примере создается три класса. В классе base определяется виртуальная функция fanc(). Затем этот класс наследуется двумя производными классами: derivedl и derivedl. Каждый из этих классов переопределяет функцию fiuicO по-своему. Внутри функции main() указатель базового класса р поочередно ссылается на объекты типа base, derivedl и derived2. Первым указателю р присваивается адрес объекта ob (объекта типа base). При вызове функции func() через указатель р используется ее версия из класса base. Следующим указателю р присваивается адрес объекта d obl и функция func()

вызывается снова. Поскольку версия вызываемой виртуальной функции определяется типом объекта, на который ссылается указатель, то вызывается та версия функции, которая переопределяется в классе derivedl. Наконец, указателю р присваивается адрес объекта d ob2, и снова вызывается функция Гилс(). При этом выполняется та версия функции fimc(), которая определена внутри класса derived!.



1 ... 97 98 99 [ 100 ] 101 102 103 ... 227

© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика