Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КЛ_Середовище.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1 Мб
Скачать
☆

1.1. Складові класу

1. Поля

Полями називаються інкапсульовані в класі дані. Поля можуть бути будь-якого типу, у тому числі - класами, наприклад:

type TMyClass = class

aIntField: Integer;

aStrField: String;

aObjField: TObject;

end;

Кожен об'єкт одержує унікальний набір полів, але загальний для всіх об'єктів даного класу набір методів і властивостей. Фундаментальний принцип інкапсуляції вимагає звертатися до полів тільки за допомогою методів і властивостей класу. Однак у Object Pascal дозволяється звертатися до полів і прямо:

type

TMyClass = class

FIntField: Integer;

FStrField: String; end;

var

aObject: TMyClass;

begin

aObject.FIntField := 0;

aObject.FStrField := 'Рядок символів';

end;

Клас-нащадок одержує всі полючи усіх своїх предків і може доповнювати їх своїми, але він не може перевизначати їхній чи видаляти.

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

2. Методи

Инкапсульовані в класі процедури і функції називаються методами. Вони з'являються так само, як і звичайні підпрограми:

type

TMyClass = class

Function MyFunc(aPar: Integer): Integer;

Procedure MyProc;

end;

Доступ до методів класу, як і до його полів, можливий за допомогою складених імен:

var

aObject: TMyClass;

begin

aObject.MyProc;

end;

Як уже говорилося, методи класу можуть перекриватися в нащадках. Наприклад:

type

TParentClass = class Procedure DoWork;

end;

TChildClass = class(TParentClass) Procedure DoWork;

end;

Нащадки обох класів можуть виконувати подібну за назвою процедуру DoWork, але, у загальному випадку, будуть це робити по-різному. Таке заміщення методів називається статичним, тому що реалізується компілятором.

У Object Pascal набагато частіше використовується динамічне заміщення методів на етапі прогону програми. Для реалізації цей метод, що заміщається в батьківському класі, повинний з'являтися як динамічний (з директивою dynamic) чи віртуальний (virtual). Зустрівши таке оголошення, компілятор створить дві таблиці -DMT (Dynamic Method Table) і VMT (Virtual Method Table) і помістить у них адреси точок входу відповідно динамічних і віртуальних методів. При кожнім звертанні до методу, що заміщається, компілятор уставляє код, що дозволяє витягти адресу крапки входу в підпрограму з тієї чи іншої таблиці. У класі-нащадку метод, що заміщується, з'являється з директивою override (перекрити). Одержавши цю вказівку, компілятор створить код, що на етапі прогону програми помістив у батьківську таблицю точку входу методу класу-нащадка, що дозволить батьку виконати потрібну дію за допомогою нового методу.

Нехай, наприклад, батьківський клас за допомогою методів show і Hide відповідно показує чи ховає щось на екрані зображення. Для створення зображення він використовує метод Draw з логічним параметром:

type

TVisualObject = class(TWinControl)

Procedure Hide;

Procedure Show;

Procedure Draw(IsShow: Boolean); virtual;

end;

TVisualChildObject = class(TVisualObject)

Procedure Draw(IsShow: Boolean); override;

end;

Реалізація методів show і Hide дуже проста:

Procedure TVisualObject.Show;

begin

Draw(True) ;

end;

Procedure TVisualObject.Hide;

begin

Draw(False) ;

end;

Методи Draw у батька і нащадка мають різну реалізацію і створюють різні зображення. У результаті батьківські методи show і Hide - ховати чи показувати ті чи інші зображення будуть у залежності від конкретної реалізації методу Draw у-кожного зі своїх нащадків. Динамічне зв'язування повною мірою реалізує поліморфізм класів.

Різниця між динамічними і віртуальними методами полягає в тому, що таблиця динамічних методів DMT містить адреси тільки тих методів, що оголошені як dynamic у даному класі, у той час як таблиця VMT містить адреси віртуальних методів не тільки даного класу, але і всіх його батьків. Значно більше по розмірі таблиця VMT забезпечує більш швидкий пошук, у той час як при звертанні до динамічного методу програма спочатку переглядає таблицю DMT в об'єкта, потім -у його батьківського класу і так далі, поки не буде знайдена потрібна крапка входу.

Методи, що динамічно перекриваються, часто можуть узагалі нічого не робити. Такі методи називаються абстрактними, вони зобов'язані перекриватися в нащадках. Програміст може заборонити виклик абстрактного методу, оголосивши його з директивою abstract. Наприклад:

type

TVisualObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); virtual; abstract;

end;

TVisualChildObject = class(TWinControl)

Procedure Draw(IsShow: Boolean); override; end;

var

aVisualObject: TVisualObject;

aVisualChild: TVisualChildObject ;

begin

aVisualObject.Show; {Помилка/ Звертання до абстрактного методу} aVisualChild.Show; {Нормальне звертання. Метод Draw у класу TVisualChildObject перекритий.)

end;

Звертання до неперекритого абстрактного методу викликає помилку періоду виконання. Зрозуміло, у грамотно складеній програмі абстрактні методи ніколи не викликаються. Класи, що містять абстрактні методи, називаються абстрактними. Такі класи інкапсулююють загальні властивості своїх неабстрактних нащадків, але об'єкти абстрактних класів ніколи не створюються і не використовуються. Для експлуатації абстрактних класів у бібліотеку класів Delphi включаються класи-нащадки, у яких перекриваються абстрактні методи батька.

До складу будь-якого класу входять два спеціальних методи -конструктор і деструктор. У класу TObject ці методи називаються create і Destroy, так само вони називаються в переважній більшості його нащадків. Конструктор розподіляє об'єкт у динамічній пам'яті і поміщає адресу цієї пам'яті в перемінну self, що автоматично з'являється в класі. Деструктор видаляє об'єкт із купи. Звертання до конструктора повинне випереджати будь-яке звертання до полів і деяких методів об'єкта. За своєю формою конструктори і деструктори є процедурами, але з'являються за допомогою зарезервованих слів constructor і Destructor:

type

TMyClass = class IntField: Integer; Constructor Create(Value: Integer);

Destructor Destroy;

end;

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

var

MyObject: TMyClass;

begin

MyObject.IntField := 0;

{ Помилка! Об'єкт не створений конструктором!}

MyObject := TMyClass.Create;

// Треба так: створюємо об'єкт

MyObject.IntField := 0;

// і звертаємося до його поля

MyObect.Free;

// Знищуємо непотрібний об'єкт

end;

У базовому класі TObject визначений метод Free, що спочатку перевіряє дійсність адреси об'єкта і лише потім викликає деструктор Destroy. Звертання до деструктора об'єкта буде помилковим, якщо об'єкт не створений конструктором, тому для знищення непотрібного об'єкта варто викликати метод Free, як це зроблено в попередньому прикладі.

Більшість конструкторів реалізують деякі дії, необхідні для правильної роботи об'єкта. Тому в конструкторі класу-нащадка варто спочатку викликати конструктор свого батька, а вже потім здійснювати додаткові дії. Виклик будь-якого методу батьківського класу досягається за допомогою зарезервованого слова inherited (успадкований):

Constructor TMyClass.Create(Value: Integer);

// Можлива реалізація конструктора

begin

Inherited Create; // Викликаємо успадкований конструктор IntField := Value; // Реалізуємо додаткові дії

end;

Деякі методи можуть викликатися без створення й ініціації об'єкта. Такі методи називаються методами класу, вони з'являються за допомогою зарезервованого слова class:

type

TMyClass = class(TObject)

class Function GetClassName: String;

end;

var

S: String;

begin

S := TMyClass.GetClassName;

end;

Методи класу не повинні звертатися до полів, тому що в загальному випадку викликаються без створення об'єкта, а отже, у момент виклику полів просто не існує. Звичайно вони повертають службову інформацію про клас - ім'я класу, ім'я його батьківського класу, адреса методу і т.п.