рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

Деструктори

Деструктори - Конспект, раздел Образование, Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ Відповідно До Принципів Об'єктно-Орієнтованого Програмування| В Класі Можна О...

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

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

Ім'я деструктора співпадає з ім'ям класу і містить додатковий символ префікса "~".

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

Наприклад, якщо клас називається CMessage, його деструктор визначається таким чином:

 

~CMessage ()

{

// код деструктора

}

 

Деструктор може виконувати будь-які завдання, необхідні для видалення об'єкту. Наприклад, конструктор класу CMessage, приведеного нижче, виділяє блок пам'яті для зберігання рядка повідомлення, а деструктор звільняє пам'ять безпосередньо перед знищенням об'єкту класу:

 

#include <string.h>

class CMessage

{

private:

char *Buffer; // зберігає рядок повідомлення

public:

CMessage () // конструктор класу

{

Buffer = new char (''); // виділення пам'яті та ініціалізація

// буфера порожнім рядком

}

~CMessage() // деструктор класу

{

delete[] Buffer; // звільнення пам'яті

}

void Display ()

{

// код для відображення на екрані вмісту змінної Buffer

}

void Set (char *String) // установка нового рядка повідомлення

{

delete[] Buffer;

Buffer = new char [strlen (String) + 1];

strcpy (Buffer, String);

}

};

 

Деструктори не можна перевантажувати через відсутність у них аргументів.

7.1.6 Виклики конструкторів і деструкторів

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

У наступному списку детально перелічені моменти виклику конструкторів і деструкторів для різного виду об'єктів.

- Об'єкт оголошений глобально (тобто зовні будь-якої функції). Конструктор викликається на самому початку програми до виклику функції main() (WinMain або _tWinMain() у програмах, що працюють в середовищі Windows), деструктор - після закінчення програми.

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

- Об'єкт оголошений локально з використанням специфікатора static. Конструктор викликається, коли потік управління вперше досягає оголошення об'єкту, деструктор — в кінці програми;

- Об'єкт створений динамічно з використанням оператора new. Конструктор викликається при створенні об'єкту, а деструктор - коли об'єкт явно знищується з використанням оператора delete. Якщо оператор delete для об'єкту не виконується, деструктор ніколи не буде викликаний.

7.1.7 Константні об'єкти і функції-члени класу

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

 

class CTest

{

public:

int A;

int B;

CTest (int AVal, int BVal)

{

A = AVal;

B = BVal;

}

};

 

Наступна інструкція створює об'єкт цього класу та ініціалізує обидві змінні-члени:

 

const CTest Test (1, 2);

Це перша й остання можливість присвоїти значення змінним об'єкту Test. Із-за специфікатора const неприпустимо присвоювання вигляду:

 

Test.A =3; // помилка - не можна змінювати змінні-члени

// об'єкту типу const

 

Існують інші варіанти оголошення об'єкту з використанням специфікатора const. Щоб продемонструвати їх, розглянемо наступне оголошення об'єкту класу CFrame:

 

const CFrame Frm(5, 5, 25, 25);

Хоча функція-член GetCoord() класу CFrame не змінює ніяких змінних-членів, компілятор не дозволить програмі викликати її для об'єкту з атрибутом const:

 

int L, Т, R, В;

Frm.GetCoord (&L &T, &R, &B); // помилка

 

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

Щоб мати можливість виклику функції GetCoord() для об'єкту типу const, потрібно включити специфікатор const в оголошення функції:

 

class CFrame

{

//...

void GetCoord (int *L, int *T, int *R, int *B) const

{

*L = Left;

*T = Top;

*R = Right;

*B = Bottom;

}

// …

};

 

Специфікатор const в оголошенні функції GetCoord() означає, що інструкції тіла функції-члена класу не змінюють змінні-члени цього класу. Якщо вона спробує це зробити, компілятор згенерує помилку при компіляції початкового коду.

Функцію GetCoord() зараз можна викликати для об'єкту типу const класу CFrame:

 

const CFrame Frm (5, 5, 25, 25);

int L, T, R, B;

Frm.GetCoord (&L &T, &R, &B); // правильно - функціяGetCoord()

// оголошена як const

 

Функцію-член класу Draw() можна також оголосити як const, оскільки вона не змінює значень змінних. Очевидно, має сенс додавати специфікатор const до всіх функцій-членів, які не модифікують поля класу, щоб користувач міг вільно викликати такі функції для об'єктів типу const. Звичайно, функцію, подібну CFrame::SetCoord(), не можна оголосити як const.

7.1.8 Статичні члени класу

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

Наприклад, в наступному класі оголошена статична змінна Count:

 

class CTest

{

public:

static int Count;

// залишок оголошення класу

};

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

Окрім оголошення статичної змінної всередині класу, її необхідно також оголосити та ініціалізувати зовні, як глобальну змінну-член| класу.

При оголошенні статичних змінних поза класом необхідно вказувати клас, в якому вони оголошені, використовуючи оператор розширення області видимості (для нашого прикладу - CTest::). Наприклад, можна оголосити й ініціалізувати змінну Count таким чином:

 

// глобальні оголошення

int CTest::Count = 0;

 

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

 

void main ()

{

CTest::Count = 1;

// …

}

 

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

Можна також визначити функцію-член| класу з використанням специфікатора static, наприклад:

 

class CTest

{

// …

static int GetCount()

{

//тіло функції

}

// …

};

 

Статична функція-член класу має наступні властивості:

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

 

void main()

{

int Count = CTest::GetCount();

// …

}

 

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

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

 

include <iostream>

using namespace std;

class CTest

{

private:

static int Count;

public:

CTest ()

{

++Count;

}

~CTest ()

{

--Count;

}

static int GetCount()

{

return Count;

}

};

 

int CTest::Count = 0;

void main ()

{

cout << CTest::GetCount () << " об'єктів існує n";

CTest Testl;

CTest *PTest2 = new CTest;

cout << CTest::GetCount () << " об'єкти існують n";

delete PTest2;

cout << CTest::GetCount () << " об'єкт існує n ";

}

 

В результаті на екран буде виведено наступне:

 

0 об'єктів існує

2 об'єкти існують

1 об'єкт існує

7.1.9 Вбудовані функції-члени класу

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

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

Наприклад, як показано нижче, функціяdivisible() за умовчанням стає вбудованою:

 

include <iostream>

using namespace std;

class CSamp

{

int i, j;

public:

CSamp(int а, int b); // оголошення конструктора

int divisible(){return !(i % j);}// функція, що вбудовується

// за умовчанням,

// оскільки дано її повне визначення

};

 

CSamp::CSamp(int а, int b) // визначення конструктора дано зовні

// класу, тому він не є вбудований

{

i = а;

j = b;

}

 

int main()

{

CSamp ob1(10, 2), ob2(10, 3);

if(ob1.divisible()) cout << "10 ділиться на 2n"; // буде видано

if(ob2.divisible()) cout << "10 ділиться на 3n"; // не буде видано

return 0;

}

 

Можна примусити компілятор розглядати функцію, задану поза оголошенням класу, як вбудовану. Для цього потрібно використати специфікатор inline при оголошенні функції всередині класу, а також при її визначенні поза класом. Наприклад, можна зробити всі функції-члени класу CStack вбудованими, оголосивши клас таким чином:

 

class CStack

{

char* v;

int top;

int max_size;

public:

class Underflow {}; // виключення (стек порожній)

class Overflow {}; // виключення (переповнення)

class Bad_size {}; // виключення (неправильний розмір)

CStack (int s) // конструктор

{

top = 0;

if (10000 < s) throw Bad_size();

max_size = s;

v = new char[s]; // розмістити елементи у вільній

// пам'яті (купі, динамічній пам'яті)

}

~CStack() // деструктор

{

delete[] v; // звільнити пам'ять для можливого

// подальшого використання

}

inline void push(char c);

inline char pop();

};

 

У цьому оголошенні конструктор і деструктор є вбудованими за умовчанням, оскільки вони визначені в оголошенні класу. Функції ж push() та pop() оголошені зі специфікатором inline. Із цим же специфікатором вони повинні бути й визначені поза оголошенням класу:

 

inline void CStack::push(char c)

{

if (top = = max_size) throw Overflow();

v(top)= c;

top = top + 1;

}

inline char CStack::pop()

{

if (top = = 0) throw Underflow();

top = top - 1;

return v(top);

}

 

Слід зазначити, що якщо специфікатор inline заданий у визначенні функції-члена класу, то в оголошенні цієї функції при оголошенні класу він може бути опущений.

7.1.10 Вказівник this

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

Припустимо, наприклад, що клас CTest містить змінну N, тоді в приведеному нижче коді спочатку буде надрукована копія змінної N об'єкту Test1, а потім - об'єкту *PTest2:

 

CTest Testl;

CTest *PTest2 = new CTest;

//...

cout << Testl.N << 'n';

cout << Test2->N << 'n';

 

При посиланні на змінну-член усередині функції-члена класу конкретний об'єкт цього класу не задається:

 

class CTest {

public:

int N;

int GETN ()

{

return N; // N задається без вказівки на об'єкт

}

};

 

Як компілятор визначає, на копію змінної N якого об'єкту посилається метод класу? Відповідь полягає в наступному.

Компілятор передає методу класу прихований вказівник на об'єкт. Функція використовує саме цей прихований вказівник для доступу до коректної копії змінних-членів.

Наприклад, у виклику:

 

Test1.GetN();

 

компілятор передає функції GetN() прихований вказівник на об'єкт Test1. Функція GetN() використовує саме цей вказівник для доступу до копії змінної N, що належить об'єкту Test1.

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

Так, функціяGetN() може бути визначена таким чином:

 

int GetN()

{

return this->N; // еквівалентно return N;

}

Ім'я змінної-члена з виразом this-> перед ним є коректним, але не переслідує ніякої мети, оскільки використання вказівника this і так мається на увазі в простому посиланні на змінну-член класу|.

Якщо потрібно дістати доступ до глобальних змінних або до функції, що має таке ж ім'я, як змінна-член або функція-член, то перед ім'ям необхідно поставити оператор розширення області видимості "::", наприклад:

 

int N = 0; // глобальна змінна N

class CTest

{

public:

int N; // змінна-член класу N

int Demo()

{

cout << ::N << 'n'; // друк глобальної змінної N

cout << N << 'n'; // доступ до змінної-члена N

// з використанням вказівника this

}

};

7.1.11 Масиви об'єктів

У мові C++ масиви об'єктів можна оголошувати звичайним способом, наприклад:

 

CFrame FrmTable[10];

У цій інструкції CFrame — це клас, оголошений і розглянутий раніше.

Масив об'єктів також можна створити динамічно:

 

CFrame *FrmTable = new CFrame[10];

 

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

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

Проте, якщо масив створений динамічно з використанням оператора new, не можна забезпечити ініціалізацію окремих елементів, оскільки компілятор завжди викликає конструктор за умовчанням для кожного елементу. Крім того, при знищенні динамічно створеного масиву потрібно використовувати оператор delete[], а не просто delete. Наприклад:

 

delete[] FrmTable;

 

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

Конструктори для елементів масиву викликаються в порядку зростання індексів елементів, а деструктори - в зворотному порядку.

7.1.12 Ієрархії об'єктів

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

Наприклад, клас COuter в наступному прикладі містить об'єкт-член класу CInner, ініціалізований в конструкторі класу COuter:

 

class CInner

{

// …

public:

CInner (int Parml, int Parm2)

{

// …

}

// …

};

 

class COuter

{

private:

CInner Inner;

public:

COuter (int P1, int P2, int P3): Inner(P1, P2)

{

// реалізація конструктора

}

// …

};

 

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

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

7.1.13 Структури і об'єднання в мові C++

У мові C++ структури мови C були розширені таким чином, що в структури, так само як і в класи, стало можливим включати функції-члени, зокрема конструктори та деструктори.

Таким чином, єдиною відмінністю між структурою і класом в мові C++ стало те, що члени класу за умовчанням є закритими, а члени структури – відкритими.

Типове оголошення структури в мові C++ має наступний формат:

 

struct type_name {

// відкриті дані і функції – члени класу

private:

// закриті дані і функції – члени класу

};

 

Слід зазначити, що так само, як і в оголошенні класу, в оголошенні структури мови C++ специфікатори доступу private, protected та public можуть повторюватися, змінюючи один одного скільки завгодно разів.

Приклад структури з розширеними можливостями:

 

struct st_type

{

st_type(double b, char *n); // конструктор класу

void show(); // метод класу

private:

double balance; // закриті змінні-члени класу

char name[40];

};

 

У мові С++ об'єднання (union) також є різновидом класу, в якому функції та дані можуть міститися як його члени. Об'єднання схоже на структуру тим, що в ньому за умовчанням всі члени відкриті до тої точки, поки в оголошенні не зустрінеться специфікатор private. Головне ж тут полягає у тому, що в C++ всі змінні, які є членами об'єднання, розташовуються в пам'яті, починаючи з однієї і тієї ж адреси, тобто перекривають одна одну. Об'єднання C++ можуть мати конструктори і деструктори.

На об'єднання в C++ накладаються декілька обмежень.

По-перше, вони не можуть бути базовими класами і не можуть бути похідними класами, як це описується в наступному підрозділі.

По-друге, об'єднання не можуть мати статичних членів.

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

Наприклад:

 

union bits

{

bits(double n);

void Show_bits();

double d;

unsigned char c[sizeof(double)];

};

 

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

7.2 Похідні класи. Спадкування

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

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

7.2.1 Похідні класи

Похідний клас успадковує всі члени базового класу. У похідний клас можна додати нові члени для його настройки відповідно до додаткових вимог.

Щоб продемонструвати сказане, припустимо, що розглянутий раніше клас CFrame уже написаний, налагоджений і використовується в програмі. Припустимо також, що окрім прозорих прямокутників у програмі потрібно відображати і заповнені вікна (прямокутники, залиті суцільним кольором). Для цього необхідно оголосити новий клас. Назвемо його, наприклад, CWindow. Він міститиме велику частину властивостей класу CFrame і деякі додаткові засоби для заливки намальованих прямокутників.

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

Наприклад, CWindow можна оголосити таким чином:

 

class CWindow : public CFrame

{

// …

};

 

Язикова конструкція :public CFrame оголошує клас CWindow як похідний від CFrame. Клас CWindow успадковує усі змінні-члени й функції класу CFrame. Іншими словами, навіть якщо визначення класу CWindow у фігурних дужках буде порожнім, він вже містить функції GetCoord(), SetCoord() і Draw(), а також змінні Left, Top, Right і Bottom, оголошені в класі CFrame. При цьому CFrame буде базовим, а клас CWindow - похідним класом.

Специфікатор public слід включати в перший рядок оголошення похідного класу, як показано в приведеному прикладі. Використання даного специфікатора призводить до того, що всі відкриті члени базового класу (оголошені як public), залишаються відкритими і в похідному класі.

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

 

class CWindow : public CFrame

{

private:

int FillColor; // зберігає колір, використовуваний для закрашення вікна

public:

void Draw (void)

{

int L, T, R, B;

CFrame::Draw ();

GetCoord (&L &T, &R, &B);

Fill ((L + R) / 2 (T + B) / 2, FillColor);

}

void SetColor (int Color)

{

FillColor = __max (0, Color);

}

};

 

Окрім успадкованих членів, CWindow містить закриту змінну FillColor для зберігання коду кольору для заливки вікна. Значення змінної FillColor встановлюється за допомогою нової відкритої функції-члена SetColor(). Клас CWindow також містить нову версію функції Draw() для малювання прозорих прямокутників з подальшою заливкою суцільним кольором.

Фактично клас CWindow включає дві версії функції Draw() - успадковану і спеціально визначену для похідного класу. При виклику функції Draw() для об'єкту класу CWindow викликається версія функції, визначена усередині класу CWindow, як показано в наступному фрагменті програми:

 

CWindow Wind;

// ...

Wind.Draw(); // виклик версії функції Draw(), визначеної всередині

// класу CWindow, оскільки об'єкт Wind має

// тип CWindow

 

Робота функції Draw(), визначеної для CWindow, починається з виклику версії функції Draw() класу CFrame, що малює прозорий прямокутник:

 

CFrame::Draw();

 

Використання в інструкції оператора розширення області видимості (CFrame::) приводить до виклику версії функції Draw(), що належить класу CFrame. Якщо ж даний оператор буде опущений, то компілятор згенерує рекурсивний виклик функції Draw(), визначеної для поточного класу CWindow. Це приклад того, як функція, визначена в рамках поточного класу, замінює собою успадковану функцію.

На наступному кроці Draw() викликає успадковану функцію GetCoord() для обчислення поточних значень координат прямокутника, а потім — функцію Fill() для заливки внутрішньої області прямокутника кольором, відповідним поточному коду кольору:

 

GetCoord (&L, &Т, &R, &В);

Fill ((L + R) /2, (Т + В) / 2, FillColor);

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

Приведемо фрагмент програми, в якому використовується клас CWindow:

 

CWindow Wind; // створення об'єкту класу CWindow

Wind.SetCoord (25, 25, 100, 100); // установка координат вікна

Wind.SetColor (5); // установка кольору вікна

Wind.Draw (); // виклик функції

// Draw() класу CWindow

7.2.2 Конструктори похідних класів

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

У прикладі, приведеному вище, програма через об'єкт класу CWindow спочатку викликає функції-члени SetCoord() і SetColor() для установки значень змінних класу, а потім викликає функцію Draw() для малювання вікна.

Щоб спростити використання класу CWindow, у відкриту частину оголошення класу необхідно додати конструктор для установки значень всіх змінних при створенні об'єкту класу:

 

CWindow (int L, int T, int R, int B, int Color):CFrame (L, T, R, B)

{

SetColor (Color);

}

 

Список ініціалізації членів класу в даному конструкторі містить виклик конструктора базового класу CFrame, якому передаються значення, що присвоюються змінним-членам.

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

Конструктор класу CWindow містить виклик функції SetColor(), яка встановлює значення змінної FillColor.

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

 

CWindow Wind (25, 25, 100, 100, 5);

Wind.Draw();

 

В оголошення класу можна також додати конструктор за умовчанням, що присвоює нульові значення всім змінним-членам класу:

 

CWindow ()

{

FillColor = 0;

}

 

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

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

Конструктор за умовчанням класу CWindow дозволяє створювати об'єкти, в яких всі змінні-члени рівні нулю, без додаткової передачі нульових значень або викликів методів класу:

 

CWindow Wind; // створений об'єкт класу CWindow, у якого всім

// змінним-членам присвоєні нульові значення

7.2.3 Порядок виклику конструкторів і деструкторів

При створенні об'єкту похідного класу компілятор викликає конструктори в наступному порядку:

- конструктор базового класу;

- конструктори всіх об'єктів-членів (тобто тих змінних-членів класу, які є об'єктами класів). Ці конструктори викликаються в порядку переліку об'єктів в оголошенні класу;

- власний конструктор класу.

Деструктори, якщо вони визначені, викликаються у зворотній послідовності.

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

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

7.2.4 Успадковані змінні

Навіть якщо клас CWindow успадковує змінні-члени Left, Top, Right і Bottom із свого базового класу, це не означає можливості прямого до них доступу, оскільки вони визначені в базовому класі як закриті. Без включення в класи додаткового спеціфікатора для доступу до змінних в похідному класі довелося б використовувати відкриту функцію GetCoord(), як і в других частинах програми. Таке обмеження доступу призводить до того, що код функцій-членів стає певною мірою неефективним.

Як альтернатива можна задати специфікатор доступу protected (захищений) замість private, як показано нижче:

 

// CFrm.h: заголовний файл класу CFrame

class CFrame

{

protected:

int Left;

int Top;

int Right;

int Bottom;

public:

CFrame ()

{

Left = Top = Right = Bottom = 0;

}

CFrame (int L, int T, int R, int B)

{

SetCoord (L, T, R, B);

}

void Draw ();

void GetCoord (int *L, int *T, int *R, int *B)

{

*L = Left;

*T = Top;

*R = Right;

*B = Bottom;

}

void SetCoord (int L, int T, int R, int B);

};

 

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

 

void main ( )

{

CFrame Frm;

Frm.Left = 10; // помилка - доступ до захищеного

// члену класу ззовні неможливий

}

 

Проте, на відміну від закритих членів класу, до захищених членів класу можна безпосередньо звертатися з класу, похідного від того класу, в якому такі члени класу оголошені. Отже, CWindow можна переписати, використовуючи прямий доступ до змінних-членів, оголошених в CFrame:

 

// CWindow.h: заголовний файл класу CWindow

#include "CFrm.h"

#include <сstdlib>

void Fill (int X, int Y, int Color);

class CWindow: public CFrame

(

protected:

int FillColor;

public:

CWindow ()

{

FillColor = 0;

}

CWindow (int L, int T, int R, int B, int Color):CFrame (L, T, R, B)

{

SetColor (Color);

}

void Draw ()

{

CFrame::Draw ( );

Fill ((Left + Right) / 2 (Top + Bottom) / 2, FillColor);

}

void SetColor (int Color)

{

FillColor =__max (0, Color);

}

};

 

В даному випадку змінну FillColor класу CWindow краще оголосити як захищену, а не як закриту. Це забезпечить доступ до неї з будь-яких класів, похідних від CWindow.

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

– Конец работы –

Эта тема принадлежит разделу:

Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ

Конспект лекцій... по дисциплiні СИСТЕМНЕ ПРОГРАМУВАННЯ...

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Деструктори

Что будем делать с полученным материалом:

Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Все темы данного раздела:

РОБОТА З ДАНИМИ
Якість організації і представлення даних, що обробляються програмами, має не менше значення, ніж хороше розбиття програм на функції, обробляючі ці дані, та реалізація алгоритмів цих функцій. Більш

Оголошення
В мові С++ кожне ім'я (ідентифікатор) слід оголосити перш, ніж воно буде використовуватися в програмі. Це означає, що потрібно вказати його тип, щоб компілятор знав, до якого виду об'єктів має відн

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

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

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

Структури мови C
Масив є сукупністю елементів одного типу, а структура є сукупністю елементів різних типів. Оголошення структури має наступний формат:   struct struct_name {

Оголошення typedef
Оголошення, що починається з ключового слова typedef, вводить нове ім'я (синонім) для типу, а не оголошує нову змінну цього типу. Формат оголошення typedef наступний:   typed

Масиви як аргументи
Якщо в якості аргументу функції задається масив, то передається вказівник на його перший елемент. Наприклад:   int strlen(const char*); void f() {

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

Макрос WINAPI
Оскільки компілятор Visual C++ більше не підтримує ключових слів __pascal, __fortran та __syscall, їх дію рекомендується емулювати за допомогою описаних вище ключових слів __cdecl, __stdcall або __

ПРОГРАМУВАННЯ ВИКЛЮЧЕНЬ
Виключною ситуацією, або виключенням (exception), називається переривання нормального потоку виконання програми у відповідь на непередбачену або аварійну подію. Події, що приводять до викл

Виключення Win32
Разом з програмними викликами виключень за допомогою інструкції throw, виключення генеруються системними програмами Win32 API у відповідь на збій апаратного або програмного забезпечення. Такі виклю

Обробка виключень Win32
Існує декілька способів обробки виключень Win32. Найбільш простий з них полягає в використанні блоку catch з трьома крапками:   try { // … // генер

МОДУЛЬНЕ ПРОГРАМУВАННЯ
Набір зв'язаних процедур разом з даними, які вони обробляють, називається модулем. Модуль – програмний фрагмент, який є будівельним блоком для побудови великих програм. Як правило, модуль складаєть

Директиви препроцесора
Існує 14 стандартних директив препроцесора:   #include #define #undef #if #ifdef #ifndef #elif #else #

Макроси
Директива #define для оголошення макросів має дві синтаксичні форми:   #define ідентифікатор макроозначення #define ідентифікатор (список параметрів) макроозначення

Конструктори
Раніше наведене оголошення класу CFrame дозволяє ініціалізувати змінні-члени об'єктів класу шляхом виклику програмою користувача функції-члена класу SetCoord(). В якості іншого, стандартно

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

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

Переваги спадкування
Як випливає з приведених вище простих прикладів, спадкування дозволяє повторно використовувати створені раніше фрагменти програм і структури даних. Це дозволяє уникнути непотрібного дублювання прог

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

УЗАГАЛЬНЕНЕ ПРОГРАМУВАННЯ
Узагальнене програмування з використанням шаблонів мови C++ полегшує генерацію сімейств функцій або класів, що оперують множинами даних різних типів. При цьому не виникає необхідності створювати ок

Клас string
У мові C++ для представлення і обробки рядків є дві можливості. По-перше, можна використовувати символьний масив, що закінчується нулем і є рядком в стилі мови C, як це й робилося практичн

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

Потоки вводу - виводу
Як вже мовилося раніше, якщо програма на мові C починає виконуватися, автоматично відкриваються три потоки: stdin, stdout і stderr.Щось схоже має місце при завантаженні програми, написаної на мові

Класи потокового вводу - виводу
У C++ ввід - вивід забезпечується підключенням до програми заголовного файлу <iostream>. Саме в цьому файлі оголошені класи, що підтримують операції вводу – виводу. Система вводу – в

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

Закриття файлу
Для закриття файлу використовується функція-член| close(). Наприклад, щоб закрити файл, пов'язаний з потоком mystream, необхідна наступна інструкція:   mystream.close ();

Контроль стану вводу - виводу
В системі вводу - виводу C++ підтримується інформація про стан після кожної операції вводу - виводу. Поточний стан потоку вводу - виводу, який зберігається в об'єкті типу iostate, є переліком, визн

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

Мова програмування Visual Basic .NET
Хоча мови програмування керований C++, С# та Visual Basic .NET (VB) семантично мають практично однакові можливості, оскільки всі вони базуються на загальній специфікації мов програмування .NET (CLS

Цикл For
В мові С++ цикл for має дещо інший синтаксис, ніж цикл For в мові VB, але концепція залишається тою ж самою, якщо не зважати на те, що операція, яка виконується в кінці кожної ітерації, має бути вк

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги