Borland C++ Builder

26.07.2015

Об’єктно-орієнтоване програмування та C++

Об’єкт — це абстрактна сутність, наділена характеристиками об’єктів навколишнього реального світу. Створення об’єктів і маніпулювання ними — це зовсім не привілей мови C++, а скоріше результат методології програмування, втілює в кодових конструкціях опису об’єктів і операції над ними. Кожен об’єкт програми, як і будь-який реальний об’єкт, що відрізняється власними атрибутами і характерною поведінкою. Об’єкти можна класифікувати за різними категоріями: наприклад, мої цифрові наручний годинник «Cassio» належать до класу годин. Програмна реалізація годин входить, як стандартний додаток, до складу операційної системи вашого комп’ютера.

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

Об’єктно-орієнтоване програмування (ООП) — це методика, яка концентрує основну увагу програміста на зв’язках між об’єктами, а не на деталях їхньої реалізації. У цій главі основні принципи ООП (інкапсуляція, успадкування, поліморфізм, створення класів і об’єктів) інтерпретуються і доповнюються новими поняттями і термінологією, прийнятими інтегрованою середовищем візуальної обробки C++Builder. Наводиться опис розширень мови новими можливостями (компоненти, властивості, обробники подій) і останніх доповнень стандарту ANSI C++ (шаблони, простору імен, явні і непостійні оголошення, ідентифікація типів при виконанні програми, виключення).

Розділ носить оглядовий характер, вона покликана познайомити читача зі спеціальною термінологією ООП, до якої автор змушений вдаватися протягом всієї книги. Це викликано тим, що C++Builder є типовою системою ООП і претендує на кульмінаційну роль в історії його розвитку.

3.1 Інкапсуляція

Інкапсуляція є об’єднання в єдиному об’єкті даних і кодів, які оперують з цими даними. У термінології ООП дані називаються членами даних (data members) об’єкта, а коди — об’єктними методами або функціями-членами (methods, member functions).

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

Іншим важливим наслідком інкапсуляції є легкість обміну об’єктами, перенесення їх з однієї програми в іншу. Простота і доступність принципу інкапсуляції ООП стимулює програмістів до розширення Бібліотеки Візуальних Компонент, що входить до складу C++Builder.

3.2 Класи, компоненти та об’єкти

Клас не має фізичної сутності, його найближчою аналогією є оголошення структури. Пам’ять виділяється тільки тоді, коли клас використовується для створення об’єкта. Цей процес також називається створенням екземпляра класу (class instance).

Будь-який об’єкт мови C++ має однакові атрибути і функціональність з іншими об’єктами того ж класу. За створення своїх класів і поведінку об’єктів цих класів повну відповідальність несе сам програміст. Працюючи в деякій середовищі, програміст отримує доступ до великих бібліотек стандартних класів (наприклад, до Бібліотеки Візуальних Компонент C++Builder).

Звичайно, об’єкт знаходиться в деякому унікальному стані, що визначається поточними значеннями атрибутів. Функціональність об’єктного класу визначається можливими операціями над екземпляр цього класу.

Визначення класу в мові C++ містить інкапсуляцію членів даних і методів, які оперують з членами даних і визначають поведінку об’єкта. Повертаючись до нашого прикладу, зазначимо, що рідкокристалічний дисплей годин «Casio» представляє член даних цього об’єкта, а кнопки управління — об’єктні методи. Натискаючи кнопки годин, можна оперувати з установками часу на дисплеї, тобто слідуючи термінології ООП, методи модифікують стан об’єкта шляхом зміни членів.

C++Builder вводить поняття компонент (components) — спеціальних класів, властивості яких представляють атрибути об’єктів, а їх методи реалізують операції над відповідними примірниками компонентних класів. Поняття метод зазвичай використовується в контексті компонентних класів і зовні не відрізняється від терміна функція-член звичайного класу. C++Builder дозволяє маніпулювати виглядом і функціональною поведінкою компонент не тільки за допомогою методів (як це роблять функції-члени звичайних класів), але і за допомогою властивостей і подій, властивих тільки класах компонент. Працюючи в середовищі C++Builder, ви напевно помітите, що маніпулювати з компонентним об’єктом можна як на стадії проектування програми, так і під час його виконання.

Властивості (properties) компонент являють собою розширення поняття членів даних і хоча не зберігають дані як такі, проте забезпечують доступ до членів даних об’єкта. C++Builder використовує ключове слово _ property для оголошення властивостей. За допомогою подій (events) компонента повідомляє користувачеві про те, що на неї чинився якийсь сприятливий вплив. Основна сфера застосування методів у програмах, розроблених в середовищі C++Builder-це обробники подій (event handlers), які реалізують реакцію програми на виникнення певних подій. Легко помітити деяку схожість подій і повідомлень операційної системи Windows. Типові прості події натискання кнопки або клавіші на клавіатурі. Компоненти інкапсулюють свої властивості, методи і події.

На перший погляд компоненти нічим не відрізняються від інших об’єктних класів мови C++, за винятком деяких особливостей, серед яких поки відзначимо наступні:

• Більшість компонент являють собою елементи керування інтерфейсом з користувачем, причому деякі мають досить складною поведінкою.

• Всі компоненти є прямими або непрямими нащадками одного загального класу-прародителя (TComponent).

• Компоненти зазвичай використовуються безпосередньо, шляхом маніпуляції з їх властивостями; вони самі не можуть служити базовими класами для побудови нових підкласів.

• Компоненти розміщуються тільки в динамічній пам’яті купи (heap) з допомогою оператора new, а не на стеку, як об’єкти звичайних класів.

• Властивості компонент укладають у собі RTTI — ідентифікацію динамічних типів.

• Компоненти можна додавати до Палітрі компонент і далі маніпулювати з ними за допомогою Редактора форм інтегрованого середовища візуальної розробки C++Builder.

ООП інтерпретує взаємодія з об’єктами як посилку запитів деякого об’єкту або між об’єктами. Об’єкт, який прийняв запит, реагує викликом відповідного методу. На відміну від інших мов ООП, таких як SmallTalk, C++ не заохочує використання поняття «запит». Запит — це те, що робиться з об’єктом, а метод — це те, як об’єкт реагує на запит.

При найближчому розгляді метод виявляється звичайною функцією-членом, яка включена у визначення класу. Щоб викликати метод, треба вказати ім’я функції в контексті даного класу або в процесорі деякої події.

Саме прихована зв’язок методу з включає класом виділяє його з поняття простої функції. Під час виконання методу він має доступ до всіх даних свого класу, хоча і не вимагає явної специфікації імені цього класу. Це забезпечується передачею кожному, без винятку, методом прихованого параметра — непостійного вказівника this на екземпляр класу. При будь-якому зверненні методу до членів даних класу, компілятор генерує спеціальний код, який використовує вказівник this.

3.3 Спадкування

Однією з самих чудових особливостей живої природи є її здатність породжувати потомство, що володіє характеристиками, подібними з характеристиками попереднього покоління. Запозичений у природи ідея спадкування вирішує проблему модифікації поведінки об’єктів і надає ООП виняткову силу і гнучкість. Успадкування дозволяє, практично без обмежень, послідовно будувати і розширювати класи, створені вами або кимось ще. Починаючи з найпростіших класів, можна створювати похідні класи по зростаючій складності, які не тільки легкі в налагодженні, але і прості по внутрішній структурі.

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

При спадкуванні базовий клас обростає новими атрибутами і операціями. У похідному класі зазвичай оголошуються нові члени даних, властивості і методи. При роботі з об’єктами програміст зазвичай підбирає найбільш відповідний клас для вирішення конкретної задачі і створює одного чи декількох нащадків від нього, які набувають здатність робити не тільки те, що закладено в батька. Дружні функції дозволяють похідним класом отримати доступ до всіх членів даних зовнішніх класів.

Крім того, похідний клас може перевантажувати (overload) успадковані методи в тому випадку, коли їхня робота в базовому класі не підходить нащадку. Використання перевантаження в ООП всіляко заохочується, хоча в прямому розумінні значення цього слова перевантажень зазвичай уникають. Кажуть, що метод перевантажений, якщо він асоціюється з більш ніж однієї однойменною функцією. Зверніть увагу, що механізм викликів перевантажених методів в ієрархії класів повністю відрізняється від викликів перевизначених функцій. Перевантаження і перевизначення — це різні поняття. Віртуальні методи використовуються для перевизначення функцій базового класу.

Щоб застосувати концепцію спадкування, наприклад, з годинником, покладемо, що дотримуючись принципу успадкування, фірма «Casio» вирішила випустити нову модель, додатково здатну, скажімо, вимовляти час при подвійному натисканні будь-якої з існуючих кнопок. Замість того, щоб проектувати заново модель говорять годин (новий клас, в термінології ООП), інженери почнуть з її прототипу (справлять нового нащадка базового класу, в термінології ООП). Похідний об’єкт успадкує всі атрибути і функціональність батьків. Вимовити синтезованим голосом цифри стануть новими членами даних нащадка, а об’єктні методи кнопок повинні бути переобтяжені, щоб реалізувати їх додаткову функціональність. Реакцією на подію подвійного натискання кнопки стане новий метод, який реалізує вимовляння послідовності цифр (нових членів даних), що відповідає поточному часу. Все вищесказане повною мірою відноситься до програмної реалізації говорять годин.

3.4 Розробка класів

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

Оголошення класу має передувати його використання. Як правило, прикладний програміст користується готовими базовими класами, причому йому зовсім не обов’язково розбиратися у всіх специфікаціях та у внутрішній реалізації. Однак, щоб використовувати базовий клас C++, треба обов’язково знати які члени даних і методи вам доступні (а якщо застосовується компоненти C++Builder — ще й надаються властивості і події).

3.4.1 Оголошення базового класу

C++Builder дає вам можливість оголосити базовий клас, який інкапсулює імена своїх властивостей, даних, методів і подій. Крім здатності виконувати свою безпосередню завдання об’єктні методи отримують певні привілеї доступу до значень властивостей і даних класу.

Кожне оголошення усередині класу визначає привілей доступу до імен класу в залежності від того, в якій секції ім’я з’являється. Кожна секція починається з одного з ключових слів: private, protected і public. Лістинг 3.1 ілюструє узагальнений синтаксис оголошення базового класу.

class className

private:

protected:

public:

“збщедоступные конструктори> загальнодоступні методи>

Лістинг 3.1. Оголошення базового класу.

Таким чином, оголошення базового класу на C++ надає наступні права доступу і відповідні області видимості:

• Приватні private імена мають найбільш обмежений доступ дозволений тільки методів даного класу. Доступ похідних класів до приватним методам базових класів заборонений.

• Захищені protected імена мають доступ дозволений методів даного і похідних від нього класів.

• Загальнодоступні public імена мають необмежений доступ дозволений методам всіх класів та об’єктів.

Наступні правила застосовуються при утворенні різних секцій оголошення класу:

1. Секції можуть з’являтися в будь-якому порядку, а їх назви можуть зустрічатися повторно.

2. Якщо секція не названа, компілятор вважає наступні оголошення імен класу приватними. Тут проявляється відмінність оголошень класу та структури — остання розглядається за замовчуванням як загальнодоступна.

3. По мірі можливості не розміщуйте члени даних у відкриту секцію, якщо тільки ви дійсно не хочете дозволити доступ до них звідусіль. Зазвичай їх оголошують захищеними, щоб дозволити доступ тільки методів похідних класів.

4. Використовуйте методи для вибірки, перевірки і встановлення значень властивостей і членів даних.

5. Конструктори і деструктори є спеціальними функціями, які не повертають значення і мають ім’я свого класу. Конструктор будує об’єкт даного класу, а деструктор його вилучає.

6. Методи (так само як конструктори і деструктори), які містять більше однієї інструкції C++, рекомендується оголошувати поза класу.

Лістинг 3.2 представляє спробу наповнити оголошення базового класу деяким конкретним змістом. Зазначимо характерне для компонентних класів C++Builder оголошення властивості Count в захищеній секції, а методу SetCount, що реалізує запис член даних FCount — в приватній секції.

class TPoint < private:

int FCount ; // Приватний член даних void _fastcall SetCount(int Value);

protected:

_property int Count = // Захищене властивість

double x; // Захищений член даних

double у; // Захищений член даних public:

TPoint(double xVal,double yVal) ; // Конструктор |

double getX(); |

double getY() ;

Лістинг 3.2. Оголошення базової компоненти TPoint.

Оголошення і визначення методів зберігаються у різних файлах (з розширеннями .h і .срр, відповідно). Лістинг 3.3 показує, що коли методи визначаються поза класу, їх імена слід кваліфікувати. Синтаксис такий кваліфікації методу, що визначає область видимості, має наступний вигляд:

( // Тіло конструктора

void _fastcall TPoint::SetCount(int Value )

if ( Value i= FCount ) // Нове значення члена даних? <

FCount = Value; // Запис нового значення Update(); // Виклик методу Update > >double TPoint::getX()

// Тіло методу getX, кваліфікованого в класі ^TPoint

Лістинг 3.3. Визначення конструктора і методів поза класу.

Після того, як ви оголосили клас, його ім’я можна використовувати як ідентифікатор типу при оголошенні об’єкта цього класу (наприклад,

TPoint* MyPoint;).

3.4.2 Конструктори і деструктори

Як випливає з назв, конструктор — це метод, який будує в пам’яті об’єкт даного класу, а деструктор — це метод, який видаляє його. Конструктори і деструктори відрізняються від інших об’єктних методів наступними особливостями:

• Мають ім’я, ідентичне імені свого класу.

• Не мають значення, що повертається.

• Не можуть успадковуватися, хоча похідний клас може викликати конструктори і деструктори базового класу.

• Автоматично генеруються компілятором як public якщо не були оголошені вами інакше.

• Автоматично викликаються компілятором, щоб гарантувати належне створення і знищення об’єктів класів.

• Можуть містити неявні звернення до операторів new і delete якщо об’єкт вимагає виділення та знищення динамічної пам’яті.

Лістинг 3.4 демонструє узагальнений синтаксис оголошень конструкторів і деструктора.

class className

// Інші члени даних className (); // Конструктор за замовчуванням | className(<список параметрів;-);// Конструктор з аргументами | className(const className&) ; // Конструктор копіювання

// Інші конструктори «className (); // Деструктор

// Інші методи >;

Лістинг 3.4. Оголошення конструкторів і деструктора.

Клас може містити будь-яке число конструкторів, у тому числі жодного. Конструктори не можуть бути оголошені віртуальними. Не кладіть всі конструктори в захищеній секції і намагайтеся зменшити їх кількість, використовуючи значення аргументів за замовчуванням. Існує три види конструкторів:

• Конструктор за замовчуванням не має параметрів. Якщо клас не містить жодного конструктора, компілятор автоматично створить один конструктор за замовчуванням, який просто виділяє пам’ять при створенні об’єкта свого класу.

• Конструктор з аргументами дозволяє ініціалізувати об’єкт у момент його створення — викликати різні функції, виділяти динамічну пам’ять, присвоювати змінним початкові значення і т. п.

• Конструктор копіювання призначений для створення об’єктів даного класу шляхом копіювання даних з іншого, вже існуючого об’єкта цього класу. Такі конструктори особливо доцільні для створення копій об’єктів, які моделюють динамічні структури даних. Однак, за замовчуванням компілятор створює так звані конструктори поверхневого копіювання (shallow copy constructors), які копіюють тільки члени даних. Тому якщо якісь члени даних містять покажчики, самі дані не будуть копіюватися. Для реалізації «глибокого» копіювання код конструктора треба включити відповідні інструкції.

Клас може оголосити тільки один загальнодоступний деструктор, імені якого, ідентичного імені свого класу, повинен передувати знак

(тильда). Деструктор не має параметрів і може бути оголошений віртуальним. Якщо клас не містить оголошення деструктора, компілятор автоматично створить його.

Зазвичай деструктори виконують операції, зворотні тим, що виконували відповідні конструктори. Якщо ви створили об’єкт класу файл, то в деструкторе цей файл, ймовірно, буде закриватися. Якщо конструктор класу виділяє динамічну пам’ять для масиву даних (за допомогою оператора new), то деструктор, ймовірно, звільнить виділену пам’ять (за допомогою оператора delete) і т. п.

3.4.3 Оголошення похідних класів

C++Builder дає можливість оголосити похідний клас, який успадковує властивості, дані, методи і події всіх своїх попередників в ієрархії класів, а також може оголошувати нові характеристики та перевантажувати деякі з успадкованих функцій. Наслідуючи вказані характеристики базового класу, можна змусити породжений клас розширити, звузити, змінити, знищити або залишити їх без змін.

Успадкування дозволяє повторно використовувати код базового класу в примірниках похідного класу. Концепція повторного використання має паралель у живій природі: ДНК можна розглядати як базовий матеріал, який кожне породжене істота повторно використовує для відтворення свого власного виду. <

Лістинг 3.5 ілюструє узагальнений синтаксис оголошення похідного класу. Порядок перерахування секцій відповідає розширень привілеїв захисту і областей видимості ув’язнених у них елементів: від найбільш обмежених до самим доступним.

class className. [^спецификатор доступу;» ] parentClass <

private:

protected:

public:

_published:

Лістинг 3.5. Оголошення похідного класу.

Відзначимо появу нової секції з ключовим словом _ published — додаток, яке C++Builder вводить в стандарт ANSI C++ для оголошення загальновідомих елементів компонентних класів. Ця секція відрізняється від загальнодоступною тільки тим, що компілятор генерує інформацію RTTI про властивості, членах-даних і методах об’єкта і C++Builder організовує передачу цієї інформації Інспектору об’єктів під час виконання програми. У розділі 6 ми зупинимося на цьому більш детально.

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

Коли клас породжується від базового, всі його імена в похідному класі автоматично стають приватними за замовчуванням. Але його легко змінити, вказавши такі специфікатори доступу базового класу:

• private. Успадковані (тобто захищені і загальнодоступні) імена базового класу стають недоступними примірниках похідного класу.

• public. Загальнодоступні імена базового класу і його попередників будуть доступними у примірниках похідного класу, а всі захищені залишаться захищеними.

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

він цілком прийнятний для вас, однак містить функцію, що вимагає невеликого доопрацювання. Написання заново потрібної функції в похідному класі є марною тратою часу. Замість цього треба повторно використовувати код в базовому класі, розширюючи його настільки, наскільки це необхідно. Просто перевизначити в похідному класі ту функцію базового класу, яка вас не влаштовує. Подібним чином можна породжувати класи, які обмежують можливості базового класу: він цілком прийнятний для вас, але робить щось зайве.

Розглянемо застосування методик розширення та обмеження характеристик на прикладі створення різновидів об’єкта кнопки — типових похідних класів, одержуваних при спадкуванні базової компоненти TButtonControl з Бібліотеки Візуальних Компонент C++Builder. Кнопки різного виду часто з’являтися в діалогових вікнах графічного інтерфейсу ваших програм.

Borland C++ Builder

Рис. 3.1 показує, що базовий клас TButtonControl здатний за допомогою батьківського методу Draw відображати кнопку у вигляді двох вкладених прямокутників: зовнішньої рамки і внутрішньої зафарбованої області.

Borland C++ Builder

Щоб створити просту кнопку без рамки (Рис. 3.2), потрібно побудувати похідний клас SimpleButton, використавши в якості батьківського TButtonControl, і перевантажити метод Draw з обмеженням його функціональності (Лістинг 3.6)

class SimpleButton:public : TButtonControl < public:

SimpleButton(int x,int y) ;

Короткий опис статті: об’єктно орієнтоване програмування

Джерело: Borland C++ Builder

Також ви можете прочитати