Складові класу
Delphi site: daily Delphi-news, documentation, articles, review, interview, computer humor.
Як мовилося раніше, складові класу часто називаються його членами.
Полями називаються інкапсульовані у класі дані. Поля можуть бути будь-якого типу, у тому числі класами, наприклад:
type TMyClass = class
Кожен об'єкт отримує унікальний набір полів, але загальний всім об'єктів даного класу набір методів і властивостей. Фундаментальний принцип інкапсуляції вимагає звертатися до полів лише за допомогою методів та властивостей класу. Однак Delphi дозволяється звертатися до полів і безпосередньо:
type TMyClass - class
Зауважу, що з побудові компонентів поля зазвичай робляться недоступними іншим компонентним класам, а доступом до них реалізується методами і властивостями класу.
Клас-нащадок отримує всі поля всіх своїх предків і може доповнювати їх своїми, але не може перевизначати їх чи видаляти. Таким чином, чим нижче у дереві ієрархії розташовується клас, тим більше даних отримують у своє розпорядження його об'єкти.
Інкапсульовані у класі процедури та функції називаються методами. Вони оголошуються так само, як і звичайні підпрограми:
type TMyClass = class
Доступ до методів класу, як і до його полів, можливий за допомогою складових імен:
Як мовилося раніше, методи класу можуть перекриватися в нащадках. Наприклад: type TParentClass = class
Нащадки обох класів можуть виконувати подібну за назвою процедуру DoWork, але в загальному випадку це робитимуть по-різному. Таке заміщення методів називається статичним, оскільки реалізується компілятором.
Нехай, наприклад, батьківський клас за допомогою методів Show та Hide відповідно показує щось на екрані або ховає зображення. Для створеннязображення він використовує метод Draw з логічним параметром:
Реалізація методів Show та Hide дуже проста:
Методи Draw у батька та нащадка мають різну реалізацію та створюють різні зображення. В результаті батьківські методи Show і Hide ховатимуть або показуватимуть ті чи інші зображення в залежності від конкретної реалізації методу Draw у будь-якого зі своїх нащадків. Динамічний зв'язування повною мірою реалізує поліморфізм класів.
Якщо якийсь метод викликається часто чи потрібно мінімізувати час звернення щодо нього, його слід оголошувати як віртуальний (virtual). Будь-які інші методи краще оголошувати динамічними (dynamic).
Методи, що динамічно перекриваються, часто можуть взагалі нічого не робити. Такі способи називаються абстрактними, вони мають перекриватися в нащадках. Програміст може заборонити виклик абстрактного методу, оголосивши його з директивою abstract. Наприклад:
TVisualChildObject = class(TWinControl) Procedure Draw(IsShow: Boolean); override; end; var aVisualObject: TVisualObject; aVisualChild: TVisualChildObject; begin aVisualObject.Show; aVisualChild.Show;
Звернення до неперекритого абстрактного методу викликає помилку періоду виконання. Зрозуміло, грамотно складеної програмі абстрактні методи ніколи не викликаються. Класи, які містять абстрактні методи, називаються абстрактними. Такі класи інкапсулюють загальні властивості своїх неабстрактних нащадків, але об'єкти абстрактних класів ніколи не створюються та не використовуються. Для експлуатації абстрактних класів до бібліотеки класів Delphi включаються класи-нащадки, у яких перекриваються абстрактні методи батька.
Будь-які поля об'єкта, а також методи класу, що оперують полями, можуть викликатися тільки після створення об'єкта викликом конструктора,оскільки конструктори розподіляють об'єкт у динамічній пам'яті і роблять дійсним покажчик, що міститься в об'єкті.
var MyObject: TMyClass; begin MyObject.IntField := 0; // Помилка! Об'єкт не створи
// Конструктором! MyObject: = TMyClass.Create; // Треба так: створюємо об'єкт MyObject.IntField: = 0; // і звертаємось до його поля
MyObect.Free; // Знищуємо непотрібний об'єкт
Спеціально для знищення об'єктів у модулі System визначено процедуру FreeAndNil, яка не тільки знищує об'єкт, а й поміщає до його покажчика (нагадаю, що ним є ідентифікатор об'єкта) значення NIL:
FreeAndNil(MyObject) Процедура FreeAndNil вперше введена у Delphi версії 5.
Більшість конструкторів реалізують деякі дії, необхідні правильної роботи об'єкта. Тому в конструкторі класу-нащадка слід спочатку викликати конструктор його батька, а вже потім здійснювати додаткові дії. Виклик будь-якого методу батьківського класу досягається за допомогою зарезервованого слова inherited (успадкований):
Value: Integer); // Можлива реалізація конструктора begin inherited Create(AOwner); // Викликаємо успадкований
// конструктор IntField: = Value; // Реалізуємо додаткові
Зауважу, що під час виклику однойменного батьківського методу можна опускати ім'я методу та параметри звернення до нього.
Деякі методи можуть викликатись без створення та ініціалізації об'єкта. Такі методи називаються методами класу, вони оголошуються за допомогою зарезервованого слова.
На відміну від інших версій Delphi, у версіях 4 і пізніших з'явилася можливість у межах одного класу мати кілька однойменних методів. Описаний вище механізм перекриття батьківського методу однойменним методом нащадкапризводить до того, що нащадок "не бачить" перекритий батьківський метод і може звертатися до нього лише за допомогою зарезервованого слова inherited. У Delphi 4 введено зарезервоване слово overlpad (перезавантажити), за допомогою якого стає видно однойменні методи як батька, так і нащадка.
Щоб однойменні методи можна було відрізнити один від одного, кожен із них повинен мати унікальний набір параметрів. У ході виконання програми при зверненні до одного з однойменних методів програма перевіряє тип та кількість фактичних параметрів обігу та вибирає необхідний метод.
У наступному прикладі в класі TForml використовуються цілих 4 однойменних методи Close. Лише один з них – успадкований метод без параметра – виконує свої основні функції – закриває вікно. Три інші відрізняються набором параметрів та виводять повідомлення у заголовок вікна.
Помістіть на порожню форму чотири кнопки TButton і напишіть такі обробники подій OnClick:
І, нарешті, у розділі implementation розмістіть описи оголошених методів:
Тепер після запуску програми три перші кнопки викликатимуть методи Close класу TForml і змінюватимуть заголовок вікна, тоді як кнопка Button4 звернеться до методу Close батьківського класу TForm і закриє вікно.
type TaClass = class
У контексті програми властивість поводиться як звичайне поле. Наприклад, ми могли б написати такі оператори:
aClass.Destroy; // Видалення непотрібного об'єкта
Різниця між цим та наступним операторами полягає в тому, що при зверненні до властивості автоматично підключається метод SetField, в якому можуть реалізовуватись специфічні дії:
На відміну від змінної, властивість не можна передати в підпрограму на ім'я, тобто як параметр var.
Якщонемає необхідності в спеціальних діях під час читання або запису властивості, замість імені відповідного методу можна вказувати ім'я поля:
type TaClass - class
IntFiled: Integers-procedure SetField(Value: Integer); property IntegerValue:
Якщо необхідно, щоб властивість була доступна тільки для читання або тільки для запису, слід опустити відповідно частину write або read. Власне кажучи, властивість може й пов'язуватися з полем. Фактично воно описує один або два методи, які здійснюють деякі дії над даними того самого типу, що і властивість.
Події займають деяке проміжне положення між властивостями і методами: з одного боку, їх, як і властивості, можна зробити доступними у вікні Інспектора об'єктів, з іншого, вони містять програмний код, який виконується у відповідь на стан програми або повідомлення Windows, що змінилося.
Технічно вони оголошуються так само, як властивості - за допомогою зарезервованого слова property. Однак типом цієї «властивості» має бути один із раніше оголошених процедурних типів.
type TEvent = procedure; MyClass = class(TComponent) protected FEvent: TEvent; public
Тепер можна розмістити подію в розділі published:
MyClass = class(TComponent) protected FEvent: TEvent; published
Якщо у вказівці процедурного типу опустити слова of object, компілятор не створить інформацію про типі часу виконання, і ми зможемо помістити опис події в будь-яку секцію, крім published. Це означає, що ми не зможемо працювати з подією за допомогою Інспектора об'єктів.