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

Конструктори, динамічні об'єкти і деструктори

Якщо в об'єктному типі є хоча б один віртуальний метод, у ньому повинний бути і спеціальний метод, відомий як конструктор, який обов’язково повинний бути застосований до екземпляра об'єкта до першого звертання до віртуального методу. У силу цього конструктор звичайно являє собою метод, що задає для об'єкта деякі початкові значення (тобто виконуючий його ініціалізацію). Конструктор може бути визначений або в даному об'єкті, або успадкований від об'єкта-предка. При цьому сам конструктор віртуальним методом бути не може.

В описі об'єктного типу заголовок конструктора відрізняється від заголовка звичайного методу тільки тим, що в ньому зарезервоване слово PROCEDURE замінене на CONSTRUCTOR. Чим реально конструктор відрізняється від звичайного методу? Конструктор, крім описаних у ньому дій, установлює зв'язок між об'єктом і спеціальною таблицею віртуальних методів, що містить адреси кодів, які реалізують віртуальні методи.

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

var

Dotl : ^Dot

Ringl : ^Ring; begin

New(Dotl, lnit);

New(Ringl, Init);

Вище згадувалося, що якщо в об'єкті маються віртуальні методи, перед звертанням до них екземпляр даного об'єкта повинний викликати конструктор. Саме таким конструктором тут є Init, що викликається процедурою New (як свій другий параметр, використовуючи при цьому розширений синтаксис). (Приклад програми, у якій екземпляри об'єктів Dotl і Ringl оголошені в якості динамічних, мається наприкінці даного розділу.)

Після завершення роботи з динамічним об'єктом виділену для нього пам'ять варто звільнити. Це виконується за допомогою стандартної процедури DISPOSE:

Dispose (Dotl, Done);

Dispose (Ringl, Done);

Тут процедура DISPOSE звертається до деструктора DONE, що зазначений у якості її другого параметра. В описі об'єктного типу заголовок деструктора відрізняється від заголовка звичайного методу тільки тим, що в ньому зарезервоване слово PROCEDURE замінене на DESTRUCTOR. Однак, на відміну від конструктора, зарезервоване слово DESTRUCTOR є синонімом PROCEDURE. Іншими словами, метод, що завершує роботу з об'єктом, оформляється спеціальним зарезервованим словом тільки для того, щоб дотримати стилістичної симетрії (якщо є спеціальний починаючий метод, логіка диктує, що повинено бути і відповідний завершальний метод). Крім того, на відміну від конструкторів, деструктори можуть являти собою віртуальні методи. При цьому необхідно розуміти що виклик деструктора Done не як параметр процедури Dispose, а самого по собі, динамічну пам'ять не звільнить.На закінчення приведемо текст програми, аналогічної попередній, у якій об'єкти Dotl і Ringl оголошені в якості динамічних.

program ObjDotCircl;

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);

destructor Done;

end;

Ring = object (Dot) ;

Rad : integer;

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

procedure Show; virtual;

procedure Hide; virtual;

destructor Done;

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,0);

end;

procedure Dot.Move;

begin

Hide;

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

Show

end;

constructor Ring.Init;

begin

a:=x;

b:=y;

Rad:=z;

end;

procedure Ring.Show;

begin

SetColor(Black);

Circle(a,b,Rad);

end;

procedure Ring.Hide;

begin

SetColor(0);

Circle{a,b,Rad);

end;

destructor Dot.Done;

begin

Hide;

end;

destructor Ring.Done;

begin

Hide;

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

New(Dotl,Init(GetMaxX div 2, GetMaxY div 2))

Dotl^.Show;

New(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:Ringr.Move(5,0);

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

end;

until a = chr(27);

Dispose (Dotl, Done);

Dispose(Ringl,Done)

end;

end.

У яких випадках має сенс використовувати динамічні змінні, ми з'ясували при вивченні покажчиків і використання динамічної пам'яті. Наприклад, якщо програма оперує множиною об'єктів (а не всього двома, як у нашому випадку) і звичайної (чи статичної) пам'яті для всіх цих об'єктів не вистачає, варто помістити їх у динамічну пам'ять. Крім того, якщо використання об'єкта починається в середині програми чи завершується задовго до її кінця, щоб не займати пам'ять увесь час, також доцільно оголосити такі об'єкти в якості динамічних. (Останнє для нашого приклада також не актуально, оскільки екземпляри об'єктів тут використовуються від початку і до кінця; тобто строго говорячи, пам'ять за допомогою деструкторів у нашій програмі можна було б і не звільняти.)