Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_Паскал.doc
Скачиваний:
2
Добавлен:
21.09.2019
Размер:
1.21 Mб
Скачать

Віртуальні методи і поліморфізм

Давайте повернемося до методу Move з попередньої програми. От його опис:

procedure Dot.Move;

begin

Hide;

a:=a+Da; b:=b+Db;

show

end;

Слід звернути увагу, на те, що з середини методу Move мають місце звертання до методів Hide і Show. Зміст цих звертань очевидний: спочатку об'єкт робиться невидимим на екрані (викликається метод Hide), потім переміщається (координати точки на екрані потрібним чином змінюються) і, нарешті, знову робиться видимим (викликається метод Show).

Але що відбудеться, якщо метод Move у даному виді використовувати в програмі з двома об'єктами: Dot і Ring? Згадаємо, що метод Move успадковується об'єктом Ring від об'єкта Dot, а методи Hide і Show, оскільки приховання і показ окружності на екрані здійснюється не так, як точки, в об'єкті Ring перевизначаються. Отож, для точки при цьому ніяких ускладнень не виникає. Точка без проблем "гасне", змінює координати і потім знову "запалюється", оскільки усі викликувані тут методи (спочатку Move, а з нього — Hide і Show) для об'єкта Dot є "рідними".

Що стосується кола, то при виклику методу Ring.Move система намагається знайти метод з таким ім'ям в описі об'єктного типу Ring і, не знайшовши його, продовжує пошук в об'єкті-предку. У результаті має місце звертання до методу Dot.Move. Після цього з методу Move повинні бути викликані (перевизначені) методи Ring.Hide і Ring.Show, однак цього не відбувається з успадкованого методу Dot.Move, екземпляр об'єкта Ringl, замість Ring.Hide і Ring.Show, викликає однойменні методи об'єкта Dot. Це пояснюється тим, що методи Dot.Move, Dot.Hide і Dot.Show жорстко зв'язані, оскільки вони були відкомпільовані в єдиному контексті — об'єктному типі Dot. Іншими словами, зв'язок між цими методами, що був встановлена при компіляції, має статичний характер. У цьому випадку виявиться переміщується також точка, а не коло. (Правда, текст відповідної програми тут не приводиться, однак читач може самостійно перевірити, що це саме так — для цього досить потрібним чином перетворити текст попередньої програми. При натисканні потрібних клавіш, що повинні б переміщати коло,, на екрані з'являється друга точка, що і переміщається замість кола.)

Як же зробити так, щоб методи Hide і Show викликалися в залежності від того, екземпляр якого об'єкта ініціював звертання до методу Move? Тут на допомогу приходить механізм, відомий як динамічне (чи пізнє) зв'язування— на відміну від статичного (чи раннього) зв'язування. Зазначений механізм реалізується за допомогою віртуальних методів.

Заголовок віртуального методу в описі об'єктного типу доповнюється зарезервованим словом VIRTUAL. Якщо в нащадках цього об'єктного типу маються перевизначальні методи (тобто методи з тим же ім'ям), вони також повинні бути оголошені як віртуальні і при цьому мати той же набір формальних параметрів, що і метод об'єкта-предка. От як виглядають опису об'єктних типів Dot і Rlng з віртуальними методами:

Dot=object

a, b :integer;

constructor Init (x,y:integer);

procedure Show; virtual;

procedure Hide; virtual;

procedure Move (Da, Db: integer); end;

Ring = object (Dot)

Rad : integer;

constructor Init (x, y, r : integer);

procedure Show; virtual;

procedure Hide; virtual;

end;

(Тут CONSTRUCTOR— це зарезервоване слово, що позначає особливий вид процедури. Виклик віртуального методу повинний бути випереджений звертанням до такої процедури.)

Тепер повернемося до виклику екземпляром об'єкта Ring методу Move, a з нього — методів Hide і Show. Тут спочатку система звертається до методу Dot.Move (попередньо метод з таким ім'ям шукається в описі об'єкта Ring і, оскільки його тут ннем, має місце звертання до відповідного методу об'єкта-предка). Після цього для переміщення кола спочатку потрібно зробити його невидимим (викликати метод Ring.Hide), потім потрібним чином змінити координати центра і, нарешті, відобразити коло у новому місці на екрані (викликати метод Ring.Show). А оскільки при виконанні програми Turbo Pascal забезпечує звертання саме до того віртуального методу, що визначений для викликаючого об'єкта, з методу Dot.Move мають місце звертання до методів Ring.Hide і Ring.Show.

На закінчення приведемо повний текст програми, що відображає на екрані крапку і коло, які можна переміщати, натискаючи відповідні клавіші.

program ObjDotCirc;

uses crt, graph;

type

Dot=object

a, b :integer;

constructor Init (x,y:integer);

procedure Show; virtual;

procedure Hide; virtual;

procedure Move (Da, Db: integer);

end;

Ring = object (Dot)

Rad : integer;

constructor Init (x, y, r : integer);

procedure Show; virtual;

procedure Hide; virtual;

end;

constructor Dot.Init; begin

a:=x; b:=y;

end;

procedure Dot.Show;

begin

PutPixel(a,b,White);

end;

procedure Dot.Hide;

begin

PutPixel(a,b,Black);

end;

procedure Dot.Move;

begin

Hide;

a:=a+Da; b:=b+Db;

Show

end;

constructor Ring.Init;

begin

a:=x;

b:=y;

Rad:=r;

end;

procedure Ring.Show;

begin

SetColor(White);

Circle(a,b,Rad);

end;

procedure Ring.Hide;

begin

SetColor(Black);

Circle(a,b,Rad);

end;

var i,j,k,Err:integer;

a:char;

Dotl:Dot; Ringl:Ring;

begin {тіло програми}

i:=detect; initgraph(i,j,");

Err:=GraphResult;

If Err <> grOK then WriteLn (GraphErrorMsg (Err))

else

begin

Dotl.Init(GetMaxX div 2, GetMaxY div 2);

Dotl.Show;

Ringl.Init(GetMaxX div 2, GetMaxY div 2, GetMaxY div 6);

Ringl.Show;

while KeyPressed do

a:=ReadKey;

repeat

begin

a:=ReadKey;

case ord(a) of

72:Dotl.Move(0,-5);

80:Dotl.Move(0,5);

77:Dotl.Move(5,0);

75:Dotl.Move(-5,0);

73:Ringl.Move(0,-5);

81:Ringl.Move(0,5);

79:Ringl.Move(5,0); 71:Ringl.Move(-5,0);

end;

end;

until a = chr(27);

end;

end.

Дана програма відрізняється від попередньої програми, тільки тим, що тут додатково оголошено тип-нащадка Ring. Крім того, методи Show і Hide обох об'єктів оголошені як віртуальні, а методи Init перетворені в конструктори

Також оператор CASE у новій програмі доповнений наступними варіантами:

73:Ringl.Move(0,-5);

81:Ringl.Move(0,5);

79:Ringl.Move(5,0);

71:Ringl.Move(-5,0);

Представлені варіанти забезпечують переміщення кола по екрану в чотирьох напрямках. Переміщення має місце при натисканнях клавіш <PgUp> (нагору), <PgDn> (униз), <End> (вправо) і <Home> (уліво), що генерують скэн-коды відповідно 73, 81, 79і 71.

У цій програмі особливу увагу варто звернути на виклики методу Move у тілі оператора CASE. У перших чотирьох випадках має місце звертання до методу Dot.Move, а в інших випадках — до методу Ring.Move. Однак ми знаємо, що метод Move— єдиний. Він оголошений в об'єкті Dot і успадкований об'єктом Rіng. Іншими словами, той самий метод Move працює по-різному (переміщає точку чи коло), у залежності від того, який об'єкт його викликає. Така властивість об'єктів називається поліморфізмом.