Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
30
Добавлен:
16.04.2013
Размер:
220.66 Кб
Скачать

Виртуальные методы

Обратите внимание, что метод TCircle.Move (рис.9) слово в слово повторяет метод TPoints.Move (см. рис.2). Поэтому встаёт вопрос, надо ли переопределять метод Move в потомке TCircle? Разве недостаточно получить его по наследству от прародителя TPoints без переопределения?

Представим себе, что TCircle унаследовал у TPoints метод Move без переопределения. Тогда схема объектного типа TCircle будет выглядеть, как на рис.12.

+---------------------------------------------------------------+

¦ +----------------------------+ TCircle ¦

¦ ¦ TPoints ¦ ¦

¦ ¦ +---------------+ ¦ ¦

¦ ¦ ¦ X ¦ ¦ ¦

¦ ¦ +---------------¦ ¦ +---------------+ ¦

¦ ¦ ¦ Y ¦ ¦ ¦ Radius ¦ ¦

¦ ¦ +---------------¦ ¦ +---------------¦ ¦

¦ ¦ ¦ Visible ¦ ¦ ¦ GetR ¦ ¦

¦ ¦ +---------------¦ ¦ +---------------¦ ¦

¦ ¦ ¦ Init +<-------+------¦ Init ¦ ¦

¦ ¦ +---------------¦ ¦ +---------------¦ ¦

¦ ¦ ¦ GetX ¦ ¦ +-->¦ Show ¦ ¦

¦ ¦ +---------------¦ ¦ ¦ +---------------¦ ¦

¦ ¦ ¦ GetY ¦ ¦ +---¦ Hide ¦ ¦

¦ ¦ +---------------¦ ¦ +---------------+ ¦

¦ ¦ ¦ Show +<-----+ ¦ ¦

¦ ¦ +---------------¦ ¦ ¦ ¦

¦ ¦ ¦ Hide +<--+ ¦ ¦ ¦

¦ ¦ +---------------¦ ¦ ¦ ¦ ¦

¦ ¦ ¦ Move +------+ ¦ ¦

¦ ¦ +---------------+ ¦ ¦

¦ +----------------------------+ ¦

+---------------------------------------------------------------+

Рис.12

Посмотрим, как будет выполняться программа, приведённая на рис.13, если в описании объектного типа TCircle отсутствует переопределение метода Move.

При выполнении оператора P.Move(50,50) вызовется метод TPoints.Hide, и точка погаснет. Затем положение точки изменится, и вызовется метод TPoints.Show, в результате точка зажжётся в новом положении. Эта последовательность вызовов отмечена на схеме (рис.14) стрелками с буквой P.

При выполнении оператора C.Move(50,50) компилятор, не найдя метод Move среди собственных методов объектного типа TCircle, продолжит поиск метода в прародительском типе TPoints. Найденный метод TPoints.Move начнет использоваться, и как видно из схемы, вызовется метод TPoints.Hide, затем поля X,Y изменятся, затем вызовется метод TPoints.Show - все это приведет к перемещению по экрану точки. (Последовательность вызовов отмечена на схеме буквой С). Окружность при этом передвинута не будет, так как методы TCircle.Hide и TCircle.Show не вызываются методом TPoints.Move.

Жесткая связь метода TPoints.Move с методами TPoints.Hide и TPoints.Show была определена на этапе компиляции. Такой механизм называется статическим или ранним связыванием, а сами методы - статическими.

+---------------------------------------------------------------+

¦ Type ¦

¦ TPoints = Class ¦

¦ X,Y :Integer; ¦

¦ Visible:Boolean; ¦

¦ Procedure Init(A,B:Integer); ¦

¦ Function GetX:Integer; ¦

¦ Function GetY:Integer; ¦

¦ Procedure Show; ¦

¦ Procedure Hide; ¦

¦ Procedure Move(DX,DY:Integer) ¦

¦ End; ¦

¦ TCircle = Class (TPoints) ¦

¦ Radius:Word; ¦

¦ Procedure Init(A,B:Integer; R:Word); ¦

¦ Function GetR:Word; ¦

¦ Procedure Show; ¦

¦ Procedure Hide; ¦

¦ {Procedure Move наследуется без переопределения}¦

¦ End; ¦

¦ {......... Описание методов (Рис.2 и 9)........} ¦

¦ Procedure TPoints.Move(DX,DY:Integer); ¦

¦ Var ¦

¦ B:Boolean; ¦

¦ Begin ¦

¦ B:=Visible; ¦

¦ If B Then Hide; ¦

¦ X:=X+DX; ¦

¦ Y:=Y+DY; ¦

¦ If B Then Show ¦

¦ End; ¦

¦ ... ¦

¦ Var ¦

¦ P:TPoints; ¦

¦ C:TCircle; ¦

¦ ... ¦

¦ BEGIN ¦

¦ P:=TPoints.Create; {Создать экземпляр точки}¦

¦ C:=TCircle.Create; {Создать экземпляр окружности}¦

¦ ... ¦

¦ P.Init(50,50); ¦

¦ P.Show; ¦

¦ P.Move(50,50); ¦

¦ ... ¦

¦ C.Init(50,50,50); ¦

¦ C.Show; ¦

¦ C.Move(50,50); ¦

¦ ... ¦

¦ P.Destroy; {Уничтожить экземпляр точки}¦

¦ C.Destroy; {Уничтожить экземпляр окружности}¦

¦ ... ¦

+---------------------------------------------------------------+

Рис.13

+---------------------------------------------------------------+

¦ TCircle ¦

¦ +--------------------------------+ ¦

¦ ¦ TPoints ¦ ¦

¦ ¦ ... ¦ ... ¦

¦ ¦ +---------------+ ¦ +---------------+ ¦

¦ ¦ P+---->¦ Show +<--+C ¦ ¦ Show ¦<--+ ¦

¦ ¦ ¦ +---------------¦ ¦ ¦ +---------------¦ ¦ ¦

¦ ¦ P+---->¦ Hide +<--¦C ¦ ¦ Hide +---+ ¦

¦ ¦ ¦ +---------------¦ ¦ ¦ +---------------+ ¦

¦ ¦ +-----¦ Move +---+ ¦ ¦

¦ ¦ +---------------+ ¦ ¦

¦ ¦ ^ ^ ¦ ¦

¦ ¦ P¦ C+----------+----------+ ¦

¦ +------------+-------------------+ C¦ ¦

+----------------+------------------------------+---------------+

¦ ¦

P.Move(50,50) C.Move(50,50)

Рис.14

Итак, если мы не хотим переопределять в потомке TCircle метод Move, полученный по наследству от прародителя TPoints, то для правильного действия метода Move необходимо:

- разорвать статистическую связь метода Move с методами TPoints.Show и TPoints.Hide;

- обеспечить возможность для Move вызывать либо методы TPoints.Show и TPoints.Hide, либо методы TCircle.Show и TCircle.Hide в зависимости от того, экземпляр какого объектного типа вызывает метод Move. Т.е. связь метода Move c методами Show и Hide должна определяться на этапе исполнения.

Такой механизм называется динамическим или поздним связыванием и осуществляется, если вызываемые методы Show и Hide объявить виртуальными.

Для того чтобы объявить метод виртуальным, необходимо его заголовок в объявлении объектного типа дополнить стандартной директивой Virtual. Для объектных типов, содержащих виртуальные методы, должны выполняться следующие условия:

- если в прародительском типе метод объявлен виртуальным, то во всех потомках одноимённые методы также должны быть объявлены виртуальными с использованием директивы Override. Кроме того все они должны иметь одинаковый набор формальных параметров;

- если объектный тип содержит виртуальные методы, то он обязательно должен также содержать хотя бы один особый метод, называемый конструктором. Его назначение будет объяснено ниже.

С учётом этих замечаний можно объявить объектные типы TPoints и TCircle так, как показано на рис.15.

+--------------------------------------------------------------+

¦ Type ¦

¦ TPoints = Class ¦

¦ X,Y:Integer; ¦

¦ Visible: Boolean; ¦

¦ Constructor Init(A,B:Integer); ¦

¦ Function GetX:Integer; ¦

¦ Function GetY:Integer; ¦

¦ Procedure Show; Virtual; ¦

¦ Procedure Hide; Virtual; ¦

¦ Procedure Move; ¦

¦ End; ¦

¦ TCircle = Class(TPoints) ¦

¦ Radius:Word; ¦

¦ Constructor Init(A,B:Integer;R:Word); ¦

¦ Function GetR:Word; ¦

¦ Procedure Show; Override; ¦

¦ Procedure Hide; Override; ¦

¦ End; ¦

+--------------------------------------------------------------+

Рис.15

Заметим прежде всего, что теперь метод Move наследуется объектным типом TCircle без переопределения. Во-вторых, на этапе компиляции не установится жестких связей между методом Move и виртуальными методами Show и Hide.

Посмотрим, как теперь будет выполняться программа, приведённая на рис.13 с учётом изменений в описаниях типов TPoints и TCircle.

Теперь при выполнении оператора P.Move(50,50) вызовутся виртуальные методы Hide и Show, относящиеся к объектному типу TPoints, т.к. экземпляр Р, вызывающий метод Move, объявлен как экземпляр типа TPoints.

При выполнении оператора С.Move(50,50) вызовутся виртуальные методы Hide и Show, относящиеся к объектному типу TCircle, т.к. экземпляр C, вызывающий метод Move, объявлен как экземпляр типа TCircle.

Выбор нужных методов происходит на этапе исполнения программы. Цепочки вызовов процедуры Move экземплярами Р и С показаны на рис.16 стрелками.

Соседние файлы в папке ВМИП