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

Лабораторна робота № 1.

Реалізація класів

Мета: навчитися організовувати класи: визначати структуру класу, типи даних та методів, організовувати необхідні конструктори та деструктори, використовувати специфікатори доступу; визначати об’єкти.

Обладнання: ПК, програмне забезпечення BorlandC++, методичні вказівки та завдання до лабораторної роботи.

Основний зміст роботи

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

Короткі теоретичні зведення

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

Клас можна визначити за допомогою конструкції:

class <ім’я класу> { <список членів класу> };

де <ім’я класу> – ідентифікатор;

<список членів класу> – визначення й описи типізованих даних та функцій класу.

Функції – це методи класу, що визначають операції над об’єктом.

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

Приклад.

class date //дата

{ int month, day, year; // поля: місяць, день, рік

void set (int,int,int); // метод – установити дату

void get (int*,int*,int*); // метод – одержати дату

void next (); // метод – установить наступну дату

void print (); // метод – вивести дату

};

class complex // комплексне число

{ double re, im;

double real () { return(re); }

double imag () { return(im); }

void set (double x, double y) { re = x; im = y; }

void print () { cout<<”re = ”<<re; cout<<“im = ”<<im; }

};

Для опису об’єкта класу (екземпляра класу) використовується конструкція:

<ім’я класу> <ім’я об’єкта>;

Приклади.

date today, my_birthday;

date *point = &today; // вказівник на об’єкт типу date

date clim[30]; // масив об’єктів

date &name = my_birthday; // посилання на об’єкт

Якщо визначення методів проводяться поза тілом класу, то використовується синтаксис:

<тип результату функції> <ім’я класу> :: <ім’я функції> (<список формальних параметрів>)

{ <оператори тіла функції> };

<ім’я класу> – задається клас якому належить даний метод.

В обумовлені об’єкти входять дані, які відповідають членам-даним класу. Методи класу дозволяють обробляти дані конкретних об’єктів класу. Звертатися до даних об’єкта і викликати функції для об’єкта можна двома способами.

По-перше, за допомогою “кваліфікованих” імен:

<ім’я об’єкта>.<ім’я даного>

<ім’я об’єкта>.<ім’я функції>

Наприклад:

complex x1, x2;

x1.re = 1.24;

x1.im = 2.3;

x2.set (5.1, 1.7);

x1.print();

Другий спосіб доступу використовує покажчик на об’єкт

<вказівник на об’єкт> –> <ім’я компонента>

Наприклад:

complex *point = &x1; // або point = new complex;

point –>re = 1.24;

point –>im = 2.3;

point –>print();

Доступ до компонентів класу. У розглянутих раніше прикладах класів компоненти класів є загальнодоступними. У будь-якому місці програми, де “видно” визначення класу, можна одержати доступ до компонентів об’єкта класу. Тим самим не виконується основний принцип абстракції даних – інкапсуляція (приховання) даних всередині об’єкта. Для зміни видимості компонент у визначенні класу можна використовувати специфікатори доступу: public, private.

Загальнодоступні або відкриті (public) компоненти доступні в будь-якій частині програми. Вони можуть використовуватися будь-якою функцією як всередині даного класу, так і функцією зовні нього. Доступ ззовні здійснюється через ім’я об’єкта:

<ім’я об’єкта>.<ім’я члена класу>;

<посилання на об’єкт>.<ім’я члена класу>;

<вказівнтк на об’єкт> -> <ім’я члена класу>;

Закриті (private) компоненти локалізовані в класі і не доступні ззовні. Вони можуть використовуватися функціями-членами даного класу і функціями-“друзями” того класу, в якому вони описані.

Змінити статус доступу до компонентів класу можна і за допомогою використання у визначенні класу ключового слова class. У цьому випадку усі компоненти класу за замовчуванням є закритими.

Приклад.

class complex // комплексне число

{ double re, im; // private за замовчуванням

public:

double real() { return(re); }

double imag() { return(im); }

void set (double x, double y) { re = x; im = y; }

void print() { cout<<”re = ”<<re; cout<<“im = ”<<im; }

};

Конструктор. Недоліком розглянутих раніше класів є відсутність автоматичної ініціалізації створюваних об’єктів. Для кожного знову створюваного об’єкта необхідно було викликати функцію типу set (як для класу complex), або явно присвоювати значення даним об’єкта. Однак для ініціалізації об’єктів класу в його визначення можна явно включити спеціальну компонентну функцію, що називається конструктором. Формат визначення конструктора наступний:

<ім’я класу> (<список форм параметрів>) { <оператори тіла конструктора> }

Ім’я цієї компонентної функції за правилами мови С++ повинно збігатися з ім’ям класу. Така функція автоматично викликається при визначенні чи розміщенні в пам’яті за допомогою оператора new кожного об’єкта класу.

Приклад.

сomplex (double re1 = 0.0, double im1 = 0.0) { re = re1; im = im1; }

Конструктор виділяє пам’ять для об’єкта та ініціалізує дані-члени класу. Конструктор має ряд особливостей:

  • конструктора не повертає значення, навіть тип void не припустимий;

  • вказівник на конструктор не може бути визначений і відповідно не можна одержати адресу конструктора;

  • конструктори не успадковуються;

  • конструктори не можуть бути описані з ключовими словами virtual, static, const, mutuable, valatile.

  • конструктор завжди існує для будь-якого класу, причому, якщо він не визначений явно, він створюється автоматично. За замовчуванням створюється конструктор без параметрів і конструктор копіювання. Якщо конструктор описаний явно, то конструктор за замовчуванням не створюється. За замовчуванням конструктори створюються загальнодоступними (public);

  • параметром конструктора не може бути його власний клас, але може бути посилання на нього (T&). Без явної вказівки програміста конструктор завжди автоматично викликається при визначенні (створенні) об’єкта. У цьому випадку викликається конструктор без параметрів. Для явного виклику конструктора використовуються дві форми:

<ім’я класу> <ім’я об’єкта> (<фактичні параметри>);

<ім’я класу> (<фактичні параметри>);

Перша форма допускається тільки при не порожньому списку фактичних параметрів. Вона передбачає виклик конструктора при визначенні нового об’єкта даного класу:

complex ss (5.9, 0.15);

Друга форма виклику приводить до створення об’єкта без імені:

complex ss = complex (5.9, 0.15);

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

<ім’я даного> (<вираз>)

Приклади.

class CLASS_A

{ int i;

float e;

char c;

public:

CLASS_A (int ii, float ee, char cc) : i(8), e(i * ee + ii ), c(сс) { }

. . .

};

Клас “символьний рядок”.

class string { char *ch; // вказівник на текстовий рядок

int len; // довжина текстового рядка

public:

// конструктори

// створює об’єкт – порожній рядок

string (int N = 80) : len(0) { ch = new char[N+1]; ch[0] = '\0'; }

// створює об’єкт по заданому рядку

string (const char *arch) { len=strlen (arch); ch=new char [len+1]; strcpy (ch,arch); }

// компонента-функції

// повертає посилання на довжину рядка

int& len_str (void) { return len; }

// повертає покажчик на рядок

char *str (void) { return ch; }

. . .

};

Тут в класі string два конструктори – функції, що перевантажуються.

За замовчуванням створюється також конструктор копіювання виду T::T(const T&), де Т – ім’я класу. Конструктор копіювання викликається кожен раз, коли виконується копіювання об’єктів, що належать класу. Зокрема він викликається:

  • коли об’єкт передається функції за значенням;

  • при побудові тимчасового об’єкта значення функції, що повертається;

  • при використанні об’єкта для ініціалізації іншого об’єкта.

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

string (const string& st) { len=strlen (st.len); ch=new char[len+1]; strcpy(ch,st.ch); }

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

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

class demo { int x;

public:

demo () { x=0; }

demo (int i) { x=i; }

};

void main()

{ class demo a[20]; //виклик конструктора без параметрів

class demo b[2] = { demo(10), demo(100) }; //явне присвоювання

}

Деструктор. Динамічне виділення пам’яті для об’єкта створює необхідність звільнення цієї пам’яті при знищенні об’єкта. Наприклад, якщо об’єкт формується як локальний всередині блоку, то доцільно, щоб при виході з блоку, коли вже об’єкт перестає існувати, виділена для нього пам’ять була повернута. Бажано щоб звільнення пам’яті відбувалося автоматично. Таку можливість забезпечує спеціальний компонент класу – деструктор класу. Його формат:

~<ім’я класу> ( ) { <оператори тіла деструктора> }

Ім’я деструктора збігається з ім’ям його класу, але добавляється префіксний символ “~” (тильда).

Деструктор не має параметрів і значення, що повертається. Виклик деструктора виконується не явно (автоматично), як тільки об’єкт класу знищується.

Наприклад, при виході за область визначення або при виклику оператора delete для вказівнтка на об’єкт.

string *p=new string (“рядок”);

delete p;

Якщо в класі деструктор не визначений явно, то компілятор генерує деструктор за замовчуванням, що просто звільняє пам’ять, зайняту даними об’єкта. У тих випадках, коли потрібно виконати звільнення й інші об’єкти пам’яті, наприклад область, на яку вказує ch в об’єкті string, необхідно визначити деструктор явно: ~string () { delete []ch; }

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

Вказівник this. Всі класи мають закритий невидимий член з ім’ям this. Це є вказівник на екземпляр даного класу в пам’яті. При створенні об’єкту класу вказівнику this автоматично присвоюється адреса початку області пам’яті, де розміщується екземпляр класу.

У нестатичного методу службове слово this позначає вказівник на об’єкт, з яким ця функція викликалася.

Функції-члени із специфікацією inline. Метод можна визначити в описі класу, у такому разі він вважається за підстановку (inline). Визначати метод в описі класу – це еквівалентно тому, щоб описувати функцію і визначати її із специфікатором inline відразу ж після опису класу. Вважається, що таке визначення функції відбувається після препроцесорної обробки до стадії синтаксичного аналізу і контролю типів.

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

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

Вказівники на компоненти-функції. Можна визначити вказівник на компоненти-функції:

<тип поверн значення> (<ім’я класу> :: *<ім’я вказывника на функцію>)

(<специф параметрів функції>);

Приклад.

double (complex :: *ptcom) (); // визначення вказівника

ptcom = &complex :: real; // настроювання вказівника

complex A (5.2, 2.7); // тепер для об’єкта А можна викликати його функцію

cout << (A.*ptcom) ();

Можна визначити також тип вказівника на функцію typedef double& (complex :: *PF) ();

а потім визначити і сам вказівник PF ptcom = &complex :: real;