ПЛАН ЛАБОРАТОРНИХ РОБІТ
Лабораторна робота № 1
Тема: Перша програма на С++
Лабораторна робота № 2
Тема: Програмування класів
Лабораторна робота № 3
Тема: Робота з конструкторами та деструкторами
Лабораторна робота № 4
Тема: Робота з успадкуванням
Лабораторна робота № 5
Тема: Дружні класи та функції (friend)
Лабораторна робота № 6
Тема: Перевантаження операцій
Лабораторна робота № 7
Тема: Шаблони
Лабораторна робота № 8
Тема: Потоки С++
Лабораторна робота № 9
Тема: Навчитися використовувати техніку обробки виключних ситуацій
Лабораторна робота № 10
Тема: Робота з ассемблерною вставкою на С++
Лабораторна робота №1
Тема: Перша програма на С++.
Мета: Навчитися користуватись оболонкою dev C++. Навчитись складати С++ програми, компілювати програму та виправляти помилки.
Завдання:
Ввести наступну програму та відкомпілювати її.
#include<stdio.h>
int main() {
рrintf(”Моя перша С программа\n”);
return 0; }
Написати програму, де визначаємо змінну цілого типу с , яка дорівнює номеру студента по журналу , виводимо її.
Написати програму, яка підраховує формулу:
S=w*c, де с та w – цілі змінні, значення яких вводиться користувачем.
Теоретичні відомості:
Змінна – це символічне позначення величини в програмі. Як ясно з назви, значення змінної (або величина, що вона позначає) під час виконання програми може змінюватися.
З погляду архітектури комп'ютера, змінна – це символічне позначення осередку оперативної пам'яті програми, у якій зберігаються дані. Зміст цього осередку – це поточне значення змінної.
У мові С++ перш ніж використовувати перемінну, її необхідно оголосити. Оголосити змінну з ім'ям x можна так:
int x;
В оголошенні першою вказується назва типу змінної int (ціле число), а потім ідентифікатор x – ім'я змінної. У змінної x є тип – у даному випадку ціле число. Тип змінної визначає, які можливі значення ця змінна може приймати і які операції можна виконувати над даної змінною. Тип змінної змінити не можна, тобто поки змінна x існує, вона завжди буде цілого типу.
Змінній можна привласнити яке-небудь значення за допомогою операції присвоювання. Привласнити – це значить встановити поточне значення змінної. По-іншому можна пояснити, що операція присвоювання запам'ятовує нове значення в комірці пам'яті, що призначена змінній.
int x; // оголосити цілу змінну x
int y; // оголосити цілу змінну y
x = 0; // привласнити x значення 0
y = x + 1; // привласнити y значення x + 1, тобто 1
Функції введення та виведення Що б там не було, але реальні програми важко уявити без використання операцій введення та виведення. Функція getchar() зчитує і повертає черговий символ з послідовності символів вхідного потоку. Якщо цю послідовність вичерпано, то функція getchar() повертає значення -1 (цьому значенню відповідає константа EOF). Функція putchar(аргумент), де аргументом є вираз цілого типу, виводить у стандартний вихідний потік значення аргументу, перетворене до типу char. Для введення та виведення більш складної інформації використовуються функції scanf() та printf().
Функція printf() призначена для виведення інформації за заданим форматом. Синтаксис функції printf():printf("Рядок формату"[, аргумент1[, аргумент2, [...]]]);Функція printf() перетворює значення аргументів до вигляду, поданому у рядку формату, "збирає" перетворені значення в цей рядок і виводить одержану послідовність символів у стандартний потік виведення. Рядок формату складається з об'єктів двох типів : звичайних символів, які з рядка копіюються в потік виведення, та специфікацій перетворення.
Кількість специфікацій у рядку формату повинна дорівнювати кількості аргументів.Приклад :#include<stdio.h>void main(){ int a=10,b=20,c=30; printf(" a==%d \n b==%d \n c==%d \n",a,b,c);} Специфікації перетворення для функції printf():%d - десяткове ціле;%i - десяткове ціле;%o - вісімкове ціле без знаку;%u - десяткове ціле без знаку (unsigned)%x - шістнадцяткове ціле без знаку;%f - представлення величин float та double з фіксованою точкою;%e або %Е - експоненціальний формат представлення дійсних величин;%g - представлення дійсних величин як f або Е в залежності від значень;%c - один символ (char);%s - рядок символів; %p - покажчик%n - покажчик%ld - long (в десятковому вигляді); %lo - long (у вісімковому вигляді);%p - виведення покажчика в шістнадцятковій формі;%lu - unsigned long.
Для введення інформації зі стандартного потоку введення використовується функція scanf().
Синтаксис :
scanf("Рядок формату",&аргумент1[,&аргрумент2[, ...]]);
Функція scanf() використовує практично той же набір символів специфікації, що і функція printf().#include <stdio.h>main()
{ int a,b,c; printf("A="); scanf("%d",&a); printf("B="); scanf("%d",&b); c=a+b; printf("A+B=%d",c); }
Хід роботи:
Увімкнути комп’ютер, знайти dev C++ або Visual C та запустити програму.
В текстовому режимі ввести вихідний код програми.
Зберегти його з розширенням *.сpp.
Відкомпілювати програму.
Розібратись з лістингом програми.
Оформити звіт.
Контрольні питання
Наведіть структуру програми на С.
Які типи даних використовуються в С?
Які функції введення/виведення ви знаєте?
Для чого використовується форматування у функції виведення?
Література
Том Сван. Освоение BORLAND C++. Практический курс. – М: Диалектика, 1996. – 489 с.
Лабораторна робота № 2
Тема: Програмування класів.
Мета: Усвідомити поняття “ Клас ”, ”дані-члени ” та ”функції-члени ”. Навчитися ними користуватися.
Завдання:
1. Визначити класи Triangle та Circle і знайти площі вказаних фігур (трикутника та кола).
2. Визначити клас трикутника де функція-член класу визначає невідому сторону трикутника (за формулою косинусів).
Теоретичні відомості
Оголошення класу
Класи С++ передбачають створення розширень системи передвизначених типів. Кожний тип класу становить множину об'єктів та операцій (правил), а також перетворень, використовуваних для утворення, маніпулювання та знищення таких об'єктів. Можуть бути оголошені похідні класи, що успадковують компоненти одного чи більше базових класів (класів, що породжують).
У С++ структури та об'єднання розглядаються як класи з певними замовчуваннями правил доступу.
Спрощений, у "першому наближенні", синтаксис оголошення класу наступний:
ключ_класу iм’я_класу
<:базовий_список> [<список_компонентів>]
Ключ_класу - це class, struct або union.
Необов’язковий базовий_список перераховує базовий клас чи класи, з яких iм’я_класу бере (або успадковує) об'єкти та правила. Якщо оголошені деякі базові класи, то клас "iм’я_класу" називається похідним класом. Базовий список містить специфікатори доступу за замовчуванням та необов’язкове їх перевизначення, які можуть модифікувати права доступу похідного класу до компонентів базових класів.
Необов’язковий список_компонентів оголошує компоненти класу (дані та функції) для імені-класу за замовчуванням та перевизначеннями специфікаторів доступу, що можуть впливати на те, які функції до яких компонентів класу можуть мати доступ.
Імена класів
Iм’я_класу - це будь-який ідентифікатор, унікальний в межах свого контексту. У класах структур та в об'єднаннях ім'я_класу може бути опущено.
Типи класів
Оголошення створює унікальний тип, тип класу "ім’я_класу". Це дозволяє вам оголошувати наступні об'єкти класу (або входження класу) наданого типу, а також об'єкти, що є похідними від цього типу (такі як покажчики, посилки, масиви об'єктів "iм’я_класу" і т. д.):
class X {....};
X x, &xr, *xptr, xarray [10];
/* чотири об'єкти : типу X, посилка на X, покажчик на X та масив елементів типу X */
struct Y {....};
Y y, &yr, *yptr, yarray [10];
// С дозволяє мати
// struct Y y, &yr, *yptr, yarray [10];
union Z {....};
Z z, &zr, *zptr, zarray [10];
// С дозволяє мати
// union Z z, &zr, *zptr, zarray [10];
Визначимо відмінність між оголошенням структур та об'єднань в С і С++: в С ключові слова struct та union обов’язкові, але в С++ вони потрібні тільки в тому випадку, коли імена класів Y і Z приховані (див. наступний розділ ).
Контекст імені класу
Контекст імені класу є локальним, з деякими особливостями, характерними для класів. Контекст імені класу починається з точки його оголошення і закінчується разом з обсяговим блоком. Ім'я класу переховує будь-який клас, об'єкт, нумератор або функцію з тим самим ім'ям в обсяговому контексті. Якщо ім'я класу оголошено у контексті, що містить оголошення об'єкту, функції чи нумератора з тим самим ім'ям, звернення до класу можливе тільки за допомогою уточненого специфікатора типу. Це означає, що з ім'ям класу треба використовувати ключ класу, class, struct або union. Наприклад:
struct S {....};
int S (struct S *Sptr);
void func( void )
{
S t; // неприпустиме оголошення: немає ключа класу
// і функції S у контексті
struct S s; // так можна : є уточнення ключом класу
S ( &s ); // так можна : це виклик функції
}
С++ дозволяє також неповне оголошення класу :
class X; // ще немає компонентів!
struct Y;
union Z;
Неповні оголошення дозволяють деякі посилання до імен класів X, Y або Z (зазвичай посилання на покажчики об'єктів класів) до того, як класи будуть повністю визначені. Зрозуміло, перш ніж ви зможете оголосити і використовувати об'єкти класів, ви повинні виконати повне оголошення класів з усіма їх компонентами.
Об'єкти класів
Об'єкти класів можуть бути привласнені (якщо не було заборонене копіювання), передані як аргументи функції, повернуті функцією (за деякими винятками) і т. д. Інші операції з об'єктами та компонентами класів можуть бути різними способами визначені користувачем, включаючи функції-компоненти та "друзів", а також перевизначення стандартних функцій та операцій при роботі з об'єктами конкретного класу. Перевизначення функцій та операцій називається перевантаженням. Операції та функції, обмежені об'єктами конкретного класу (або взаємозв’язаної групи класів) називаються функціями-компонентами даного класу. С++ має механізм, що дозволяє викликати те ж саме ім'я функції чи операції для виконання іншого завдання, залежно від типу чи числа аргументів чи операндів.
Список компонентів класу
Необов’язковий список компонентів становить послідовність оголошень даних (будь-якого типу, включаючи нумератори, бітові поля та інші класи) і оголошень та визначень функцій, кожне з яких може мати необов’язкові специфікатори класу пам'яті та модифікатори доступу. Визначені таким чином об'єкти називаються компонентами класу. Специфікатори класу пам'яті auto, extern та register у даному випадку недопустимі. Компоненти можуть бути оголошені зі специфікаторами класу пам'яті static.
Функції-компоненти
Функція, яка оголошена без специфікатора friend, називається функцією-компонентом класу. Функція, що оголошена з модифікатором friend, називається "функцією-другом".
Одне й те ж саме ім'я може використовуватися для позначення більш ніж однієї функції, за умови, що вони відмінні по типам або числу аргументів.
Ключове слово this
Нестатичні функції-компоненти працюють з об'єктами типу класу, з якими вони викликаються. Наприклад, якщо x - це об'єкт класу X, а f - це функція-компонент X, то виклик функції x.f() працює з x. Аналогічним чином, якщо xptr є покажчик об'єкту X, то виклик функції xptr ->f() працює з *xptr. Звідки f може знати, з яким X працювати? С++ передає f покажчик на X, що називається this. This передається як прихований аргумент при виклику нестатичних функцій-компонентів. Ключове слово this становить локальну змінну, доступну в тілі нестатичної функції-компоненту. This не потребує об'явлень, і на нього рідко зустрічаються явні посилки у визначенні функції. Однак, воно неявно використовується у функції для посилки до компонентів. Якщо, наприклад, викликається x.f(y), де y є компонентом X, то this встановлюється на &x, а y встановлюється на this->y, що еквівалентно x.y.
Функції що вбудовуються (inline)
Функція-компонента може бути оголошена в межах свого класу, але визначена будь-де в іншому місці. І навпаки, функція-компонента може бути і оголошена, і визначена у своєму класі, і тоді вона називається функцією, що вбудовується. У деяких випадках Turbo C++ може зменшити витрати часу на виклик функції, підставляючи замість виклику функції безпосередньо зкомпільований код тіла функції. Цей процес, що називається вбудованим розширенням тіла функції, не впливає на контекст імені функції чи її аргументів. Вбудоване поширення не завжди корисне і бажане. Специфікатор inline становить собою запит (або вимогу) компілятору, в якому ви повідомляєте, що вбудовані поширення бажані. Як і в разі специфікатора класу пам'яті register, компілятор може або задовольнити, або проігнорувати ваше побажання.
Явні чи неявні запити inline краще за все резервувати для функцій невеликих за обсягом та функцій, що часто викликаються, таких як функції типу operator, що реалізують перевантажені оператори. Наприклад, наступне оголошення класу :
int i; // global int
class X {
char* i;
public:
char* func (void) {return i;} // inline за замовчуванням char* i;
};
еквівалентно
inline char* X::func (void) {return i;}
func визначається "поза" класом з явним специфікатором inline. Змінна i, яку повертає func, єchar* i класу X.
Статичні компоненти
Специфікатор класу пам'яті static може бути використаний в оголошеннях компонентів даних та функцій-компонентів класу. Такі компоненти називаються статичними і мають властивості, відмінні від властивостей нестатичних компонентів. В разі нестатичних компонентів для кожного об'єкту класу "існує" окрема копія; у випадку ж статичних компонентів існує тільки одна копія, а доступ до неї виконується без посилки на який-небудь конкретний об'єкт класу. Якщо х - це статичний компонент класу Х, то до нього можна звернутися як Х::х (навіть якщо об'єкти класу Х ще не створені). Однак, можна виконати доступ до х і за допомогою звичайних операцій доступу до компонентів. Наприклад, у вигляді y.x та yptr->x, де y - це об'єкт класу X, а yptr - це покажчик об'єкту класу X, хоч вирази y та yptr ще не обчислені. Зокрема, статична функція-компонент може бути викликана як з використанням спеціального синтаксису виклику функцій-компонентів, так і без нього.
class X {
int member_int;
public:
static void func ( int i, X* ptr );
};
void g (void);
{
X obj;
func (1, &obj); // помилка, якщо де-небудь ще
// не визначена глобальна func ()
X::func (1, &obj); // виклик static func () в X
// допустимий тільки для статичних функцій
obj.func (1, &obj) // те ж саме (допустимо як для статичних, так
// і нестатичних функцій)
}
Оскільки статична функція-компонент може викликатися без урахування якого-небудь конкретного об'єкту, вона не має покажчику this. Наслідок із цього такий, що статична функція-компонент не має доступу до нестатичних компонентів без явного задання об'єкту за допомогою “.” або “->”. Наприклад, з урахуванням оголошень, зроблених у попередньому прикладі, func може бути визначена наступним чином:
void X::func(int i, X* ptr)
{
member_int = i; // на який об'єкт посилаєтся member_int? Помилка!
ptr->member_int = i; // так можна : тепер ми знаємо!
}
Без урахування вбудованих функцій, статичні функції-компоненти глобальних класів мають зовнішній тип компоновки. Статичні функції-компоненти не можуть бути віртуальними функціями. Неприпустимо мати статичну та нестатичну функції-компоненти з однаковими іменами і типами аргументів.
Оголошення статичного компоненту даних в оголошенні класу не є визначенням, тому де-небудь ще повинно бути визначення, що відповідає за розподіл пам'яті та ініціалізацію. Визначення статичного компоненту даних може бути опущене, якщо діє засіб "ініціалізації нулями за замовчуванням".
Статичні компоненти класу, який оголошений локальним по відношенню до деякої функції, не мають типу компоновки і не можуть бути ініціалізовані. Статичні компоненти глобального класу можуть бути ініціалізовані подібно звичайним глобальним об'єктам, але тільки в контексті файлу. Статичні компоненти підлягають звичайним правилам доступу до компонентів класу, за винятком того, що вони можуть бути ініціалізовані.
class X {
...
static int x;
...
};
int X::x = 1;
Головне використання статичних компонентів складається в тому, щоб відслідковувати дані, спільні для всіх об'єктів класу, як наприклад, число створених об'єктів, або ресурс, що використовувався останнім з пула, що розділяється всіма подібними об'єктами. Статичні компоненти використовуються також для:
- зменшення числа видимих глобальних імен
- того, щоб зробити очевидним, які саме статичні об'єкти якому класу належать
- дозволу управління доступом до їх імен.
Контекст компоненту
Вираз X::func() у прикладі, що наведений у розділі “Функції, що вбудовуються”, використовує ім’я класу X з модифікатором контексту доступу, що означає, що func, хоча і визначена "поза" класом, в дійсності є функцією-компонентом Х та існує в контексті Х. Вплив Х:: розповсюджується на тіло визначення цієї функції. Це пояснює, чому значення, що повертає функція, відноситься до X::i, char* i з Х, а не до глобальної змінної int i. Без модифікатора Х:: функція func представляла б звичайну функцію, що не відноситься до класу і повертає глобальну змінну int i.
Отже, всі функції-компоненти знаходяться у контексті свого класу, навіть якщо вони визначені поза цим класом.
До компонентів даних класу Х можна звертатися за допомогою операцій вибору та -> (як і в структурах С). Функції-компоненти можна викликати також за допомогою операцій вибору. Наприклад,
class X {
public:
int i;
char name [20];
X *ptr1;
X *ptr2;
void Xfunc (char *data, X* left, X* right);// визначення знаходиться не тут
};
void f (void)
{
X x1, x2, *xptr=&x1;
x1.i = 0;
x2.i = x1.i;
xptr->i = 1;
x1.Xfunc ("stan", &x2, xptr);
}
Якщо m є компонентом чи базовим компонентом класу Х, то вираз Х::m називається кваліфікованим ім'ям; воно має той же тип, що й m, і є виразом, що іменує тільки в тому випадку, якщо виразом, що іменує є m. Ключовий момент тут полягає в тому, що навіть якщо ім'я класу Х приховано іншим ім'ям, кваліфіковане ім'я Х::m забезпечить доступ до потрібного імені класу, m.
Компоненти класу не можуть додаватися до нього в іншому розділі вашої програми. Клас Х не може містити об'єкти класу Х, але може містити покажчики чи посилки на об'єкти класу Х (відзначимо аналогію з типами структур та об'єднань С).
Хід роботи:
Увімкнути комп’ютер, знайти dev C++ або Visual C та запустити програму.
В завданні №1 написати програму, яка визначає 2 окремі класи
В Triangle обчислити площу трикутника, в класі Circle – площу кола.
Вивести обчисленні значення на екран.
В завданні №2 визначити клас трикутника, задати функцію, яка визначає невідому сторону трикутника. Для цього використати формулу
Відкомпілювати програму.
Оформити звіт.
Контрольні питання :
1. Що називається класом?
2. Які специфікатори доступу ви знаєте?
3. Яку роль відіграє специфікатор доступу?
4. Що треба створити для того, щоб використати клас?
5. Що представляє собою об’єкт класу?
6. За допомогою яких операцій можна звертатись до компонентів даних та функцій класу?
7. Які функції класу називають вбудованими?
8. Для чого потрібні неповні оголошення класу?
9. Які дії можна виконувати над об’єктами класів?
10. Розкажіть про ключове слово this
11. Які компоненти називають статичними?
Література:
Том Сван. Освоение BORLAND C++. Практический курс. – М: Диалектика, 1996. – 489 с.
Шилдт Г.: Пер. с англ. –М.: Издательский дом “Вильямс”, 2002. –704 с
Лабораторна робота № 3
Тема: Робота з конструкторами та деструкторами
Мета: Вивчити призначення конструкторів та деструкторів та навчитися ними користуватися.
Завдання:
1. Дана програма:
#include <іоstream.h>
main( )
{ cout << “ Привіт! \n“;
return 0;
}
Модифікуйте її так, щоб вона виводила на виході:
Ініціалізація
Привіт!
Очищення
Не змінюйте main( )!!!.
Є клас з ім’ям Tfruit та об’єкт orange типу Tfruit. Використовуйте orange для ініціалізації нового об’єкту на ім’я grapefruit за допомогою конструктора копіювання.
Теоретичні відомості
Основні властивості конструкторів та деструкторів
Існує декілька спеціальних функцій-елементів, що визначать, яким чином об'єкти класу створюються, ініціалізуються, копіюються та руйнуються. Конструктори та деструктори є найбільш важливими з них. Вони володіють більшістю характеристик звичайних функцій-елементів: ви повинні оголосити і визначити їх в межах класу, або оголосити їх у класі, а визначити поза ним. Однак, вони володіють і деякими унікальними властивостями.
1. Вони не мають оголошень значень повернення (навіть void).
2. Вони не можуть бути успадковані, хоча похідний клас може викликати конструктори та деструктори базового класу.
3. Конструктори, як і більшість функцій мови С++, можуть мати аргументи за замовчуванням чи використовувати списки ініціалізації елементів.
4. Деструктори можуть мати атрибут virtual, але конструктори не можуть.
5. Ви не можете працювати з їхніми адресами :
main ( )
{
....
void *ptr = base::base; // неприпустимо
....
}
6. Якщо конструктори та деструктори не були задані явно, то вони можуть генеруватися Borland C++. Часто вони також можуть запускатися за відсутності явного виклику у вашій програмі. Будь-який конструктор чи деструктор, що створюється компілятором, повинен мати атрибут public.
7. Викликати конструктор таким же чином, як і звичайну функцію, не можна. Виклик деструктора допустимий тільки з повністю уточненим ім'ям.
{...
X *p;
....
p->x::~x ( ); // допустимий виклик деструктора
X::X ( ); // неприпустимий виклик конструктора
....
}
8. При визначенні та знищенні об'єктів компілятор виконує виклик конструкторів та деструкторів автоматично.
9. Конструктори та деструктори при необхідності розподілу пам'яті об'єктові можуть виконувати неявні виклики операцій new та delete.
10. Об'єкт з конструктором чи деструктором не може використовуватися у вигляді елементу об'єднання.
Якщо клас Х має один чи більше конструкторів, то один з них запускається кожного разу при визначенні об'єкту х класу Х. Конструктор створює об'єкт х та ініціалізує його. Деструктори виконують зворотній процес, руйнуючи об'єкти класу, створені конструкторами.
Конструктори активізуються також при створенні локальних або тимчасових об'єктів даного класу. Деструктори активізуються кожного разу, коли ці об'єкти виходять з області дії.
Конструктори
Конструктори відрізняються від інших функцій-елементів тим, що мають те ж саме ім'я, що і клас, до якого вони відносяться. При створенні чи копіюванні об'єкту даного класу відбувається неявний виклик відповідного конструктора.
Конструктори глобальних змінних викликаються до виклику функції main(). При використанні стартової директиви pragma для встановлення деякої функції до функції main() конструктори глобальних змінних викликаються до функцій початкового завантаження.
Локальні об'єкти створюються, як тільки стає активною область дії змінної. Конструктор також запускається при створенні тимчасового об'єкту даного класу.
class X {
public :
X ( ); // конструктор класу Х
};
Конструктор класу Х не може сприймати Х як аргумент:
class X {
....
public
X (X); // неприпустимо
}
Параметри конструктора можуть бути будь-якого типу, за винятком класу, елементом якого є даний конструктор. Конструктор може приймати у вигляді параметра посилку на свій власний клас. В такому випадку він називається конструктором копіювання. Конструктор, що не має параметрів взагалі, називається конструктором, що використовється за замовчуванням.
Конструктор, який використовується за замовчуванням
Конструктором, що використовується за замовчуванням для класу Х називається такий конструктор, що не має ніяких аргументів: Х::Х( ). Якщо для класу не існує конструкторів, що визначаються користувачем, то Borland C++ генерує конструктор за замовчуванням. При таких оголошеннях, як Х х, конструктор за замовчуванням створює об'єкт х.
Як і всі функції, конструктори можуть мати аргументи, що використовуються за замовчуванням. Наприклад, конструктор:
X::X ( int, int = 0 );
може мати один чи два аргументи. Якщо даний конструктор буде представлений тільки з одним аргументом, другий аргумент, якого не вистачає, буде прийнятий як int 0. Аналогічним чином, конструктор:
X::X ( int = 5, int = 6 )
може мати два аргументи, один аргумент, або не мати аргументів взагалі, причому в кожному випадку використовуються відповідні замовчування. Однак, конструктор за замовчуванням Х::Х ( ) не має аргументів взагалі, і його не треба плутати з конструктором, наприклад, X::X ( int = 0 ), що може або мати один аргумент, або не мати аргументів.
При виклику конструкторів треба уникати неоднозначностей. У наступному прикладі можливе неоднозначне сприйняття компілятором конструктора, що використовується за замовчуванням та конструктора, що сприймає цілочисельний параметр :
class X {
public :
X ( );
X ( int i = 0 );
};
main ( )
{
X one ( 10 ); // припустимо: використовується X::X ( int )
X two; // так не можна; неоднозначно задано,
// використовується X::X ( ) або X::X( int = 0 )
....
return 0;
}
Конструктор копіювання
Конструктор копіювання для класу Х - це такий конструктор, що може викликатися з одним-єдиним аргументом типу X: X::X ( const X& ) або X::( const X&, int = 0 ). У конструкторі копіювання припустимими також є аргументи за замовчуванням. Конструктори копіювання запускаються при копіюванні об'єкту даного класу, зазвичай в разі оголошення з ініціалізацією об'єктом іншого класу:
X x = y;
Якщо такий конструктор необхідний, але у класі Х не визначений, Borland C++ генерує конструктор копіювання для класу X автоматично.
Перевизначення конструкторів
Конструктори можна перевизначити, що дозволяє створювати об'єкти залежно від значень, що використовувалися при ініціалізації.
class X {
int integer_part;
double double_part;
public :
X ( int i ) { integer_part = i; }
X ( double d ) { double_part = d }
};
main ( )
{
X one (10); // викликає X::X ( int ) і встановлює
// integer_part у значення 10
X one (3. 14); // викликає X::X ( double ) і встановлює
// double_part у значення 3. 14
....
return 0;
}
При наявності конструктора класу об'єкти або ініціалізуються, або мають конструктор за замовчуванням. Останній використовується в разі об'єктів без явної ініціалізації.
Об'єкти класів із конструкторами можуть ініціалізуватися за допомогою списків, що задаються у круглих дужках ініціалізаторів. Цей список використовується як список аргументів, що передаються конструкторові. Альтернативним засобом ініціалізації є використання знаку рівності, за яким слідує окреме значення. Це окреме значення може мати тип першого аргументу, що сприймається конструктором даного класу. В цьому випадку додаткових аргументів або не існує, або вони мають значення за замовчуванням. Значення може також бути об'єктом даного типу класу. У першому випадку для створення об'єкту викликається відповідний конструктор. В останньому випадку для ініціалізації об'єкту викликається конструктор копіювання.
class X
{
int i;
public :
X ( ); // тіла функцій для ясності опущені
X ( int x );
X ( const X& );
};
main ( )
{
X one; // запуск конструктора за замовчуванням
X two(1); // використовується конструктор X::X (int)
X three = 1; // викликає X::X (int)
X four = one; // запускає X::X (const X&) для копіювання
X five(two); // викликає X::X (cont X&)
....
}
Конструктор може привласнювати значення своїм елементам наступним чином: він може приймати значення в якості параметрів і виконувати привласнення елементам змінним власне у тілі функції конструктора :
class X
{
int a, b;
public :
X ( int i, int j ) { a = i; b = j }
};
Деструктори
Деструктор класу викликається для звільнення елементів об'єкту до знищення самого об'єкту. Деструктор - це функція-елемент, ім'я якої співпадає з ім'ям класу, перед яким стоїть символ тильди ( ~ ). Деструктор не може приймати ніяких параметрів, а також не оголошує типу або значення, що повертається.
class X
{
public :
~X ( ); // деструктор класу X
};
Якщо деструктор не оголошений для класу явно, компілятор генерує його автоматично.
7 Виклик деструкторів
Виклик деструктора виконується неявно, коли змінна виходить зі своєї оголошеної області дії. Для локальних змінних деструктори викликаються, коли перестає бути активним блок, в якому вони оголошені. В разі глобальних змінних деструктори викликаються як частина процедури виходу після функції main().
Коли покажчики об'єктів виходять за межі області дії, неявний виклик деструктора не відбувається. Це означає, що для знищення такого об'єкту операція delete повинна задаватися явно.
Деструктори викликаються суворо у зворотній послідовності щодо послідовності виклику відповідних їм конструкторів.
8 atexit, #pragma exit і деструктори
Всі глобальні об'єкти залишаються активними доти, доки не будуть виконані коди в усіх процедурах виходу. Локальні змінні, включаючи ті, що оголошені у функції main(), знищуються при їх виході з області дії. Послідовність виконання в кінці програми Borland C++ у цьому сенсі така:
виконуються функції atexit у послідовності їх вставлення в програму;
виконуються функції #pragma exit відповідно до кодів їх пріоритетів;
викликаються деструктори глобальних змінних.
exit і деструктори
При виклику exit з програми деструктори для будь-яких локальних змінних у поточній області дії не викликаються. Глобальні змінні знищуються у звичайній послідовності.
abort і деструктори
При виклику abort будь-де з програми деструктори не викликаються, навіть для змінних глобальної області дії.
Деструктор може також викликатися явно, одним з двох способів:
непрямо, через виклик delete;
безпосередньо, шляхом завдання повністю уточненого імені деструктора.
delete можна використовувати для знищення тих об'єктів, для яких пам'ять розподілялася за допомогою new. Явний виклик деструктора необхідний