1   2   3   4
Ім'я файлу: prog_lecture_07.pdf
Розширення: pdf
Розмір: 205кб.
Дата: 29.09.2021
скачати

7
Класи
23
double Re;
double Im;
public:
static int counter;
TNumber(double x, double y):Re(x),Im(y)
{ counter++;printf("Ctor %d\n",counter);}
TNumber(TNumber& z)
{ Re = z.Re; Im = z.Im; counter++; printf("Copy %d\n",counter);}

TNumber(){printf("Dtor %d\n",counter);counter--;}
friend class TFunction; //
Оголошення дружнього класу
};
int TNumber::counter = 0;
int main()
{
printf("Start main\n");
TNumber z(1,1);
TFunction a;
a.print(z);
printf("End main\n");
return 0;
}
void TFunction::print(TNumber z)
{
printf("z = %lf + i*%lf\n", z.Re, z.Im);
}
Варто мати на увазі, що члени класу
TFunction просто мають доступ до членів класу
TNumber
, але члени класу
TNumber від цього не стають членами класу
TFunction
Іноді два класи настільки тісно зв'язані, що їм необхідний повний доступ до всіх членів. У цьому випадку їх оголошують взаємно дружніми.
Використання взаємно дружних класів
#include
class TNumber;
class TFunction
{
void print(TNumber z);
friend class TNumber;
};
class TNumber
{
double Re;
double Im;
public:
static int counter;
TNumber(double x, double y):Re(x),Im(y)
{ counter++;printf("Ctor %d\n",counter);}
TNumber(TNumber& z)
{ Re = z.Re; Im = z.Im; counter++; printf("Copy %d\n",counter);}
TNumber(){printf("Dtor %d\n",counter);counter--;}
void sum(TNumber y, TFunction z)
{Re = Re + y.Re; Im = Im + y.Im; z.print(*this);}
friend class TFunction;
};
int TNumber::counter = 0;
int main()
{
printf("Start main\n");

Лекція
7
Класи
24
TNumber z(1,1),x(1,1);
TFunction a;
z.sum(z,a);
printf("End main\n");
return 0;
}
void TFunction::print(TNumber z)
{
printf("z = %lf + i*%lf\n", z.Re, z.Im);
}
У ході виконання цієї програми на екрані з'являться наступні рядки.
Start main
Ctor 1
Ctor 2
Copy 3
Copy 4
z = 2.000000 + i*2.000000
Dtor 4
Dtor 3
End main
Dtor 2
Dtor 1
Як бачимо, функція sum()
, що є членом класу
TNumber
, одержує вільний доступ до функції print()
,
оголошеної в закритому розділі класу
TFunction
. Якби класи
TNumber
і
TFunction не були взаємно дружніми, це було б неможливо.
7.4.
РЕЗЮМЕ
 В основу об'єктно-орієнтованого програмування покладене поняття об'єкта і принципи
інкапсуляції, прихованняінформації, абстракціїданих, успадкування і поліморфізму.
 Основна ідея об'єктно-орієнтованого програмування полягає в тім, що всі обчислення відбуваються усередині об'єктів, що обмінюються між собою повідомленнями.
 Відповідно до принципу інкапсуляції об'єктцеоб'єднанняданихіалгоритмів, щообробляютьці
дані.
 В об’єктно-орієнтованих мовах існує можливість перевантажувати операції і функції, що дозволяє
об'єкту самому конкретизувати їхній зміст у ході виконання програми. »Що дозволяє спростити програмування. Наприклад, якщо перевантажити оператор
+
, то операція a + b може мати різний сенс. Якщо ці об'єкти — числа, застосовується звичайна операція додавання, якщо матриці —
матрична (конечно, для цього необхідно визначити цю операцію). У цьому (і не тільки) полягає
сутність поліморфізму. У принципі поліморфізм виявляється в трьох ситуаціях: при перевантаженні
функцій, при перевантаженні операторів і при пізнім зв'язуванні.
 Об'єкт — це фізична сутність, що виникає при виконанні програми, тобто сукупність комірок пам'яті, що зберігають дані і код. Для того щоб створити об'єкт, необхідна його схема: які дані він містить, які функції обробляють ці дані, як організований доступ до цим даних і функцій, що називаються членамиоб'єкта. У мові С++ схемою об'єкта називається клас.
 Для оголошення класу в мові С++ призначене ключове слово class
. Усе, що розташовано між фігурними дужками, що випливають за цим ключовим словом, являє собою тілокласу, що складає з його членів. Варто пам'ятати, що клас — це логічна схема об'єкта. Отже, виділення пам'яті
відбудеться лише тоді, коли об'єкт буде визначений. Оголошення класу виглядає так:
class
ім'я{
члени класу;
private:
члени класу;
protected:
члени класу;
public:
члени класу;
} <списокоб'єктів>;
 Тіло класу розділяється на три розділи, позначені наступними ключовими словами
(специфікаторами доступу).
public //
Відкритий розділ private //
Закритий розділ

Лекція
7
Класи
25
protected //
Захищений розділ
 Ключове слово public позначає розділ, у якому розміщаються функції і дані, доступні з будь-якої
точки програми, — відкриті члени. Ключове слово private оголошує розділ, у якому розташовані
закриті члени класу, доступні лише функціям-членам самого класу, а також дружнім функціям і
класам. Зверніть увагу на те, що функції-члени можуть знаходитися в ніби безіменному розділі.
Насправді, за замовчуванням функції і дані, оголошені в такому розділі, є закритими. Зміст специфікатора protected буде розкритий пізніше при описі спадкування класів.
 З класами також тісно зв'язані об'єднання. Оскільки цей вид структури більш складний і зв'язаний з досить хитромудрим розподілом пам'яті, на нього поширюються деякі обмеження. Зокрема, до об'єднань не можна застосовувати механізм успадкування; вони не можуть містити віртуальні
функції, а також статичні змінні і посилання. Крім того, об'єднання не повинні містити об'єкти класів з перевантаженим оператором присвоювання, а також явно визначеними конструкторами і
деструктором.
 Порядок специфікаторів доступу не регламентується — вони можуть повторюватися неодноразово й у будь-якому порядку. Кожен наступний специфікатор припиняє дія попереднього. Як правило,
відкриті члени оголошуються останніми, але це відноситься до питань стилю, а не до синтаксичних правил.
 Функції-члени, що не змінюють поля класу, називаються функціями-членамидоступу.
 Функції-члени, що змінюють поля класу, називаються функціями-членами, що модифікують.
 Відкриті функції члени утворюють інтерфейс класу.
 Для звертання до членів класу використовується оператор доступу до членів класу «
».
Z.init(); //
Виклик функції
- члена init()
Z.print(); //
Виклик функції
- члена print()
 Якщо в програмі оголошений вказівник на об'єкт класу, використовується оператор посилання на члени класу «
->
».
 При оголошенні полів класу не можна користатися ключовими словами auto
, extern
і
register
 Якщо визначення функції-члена міститься усередині класу, сама функція автоматично вважається що підставляється (хоча і не стає нею автоматично). Ключове слово inline для цього вказувати необов'язково, хоча і не заборонено.
 При виклику функція-член одержує вказівник на об'єкт, що зробив виклик. Цей вказівник позначається ключовим словом this
. Отже, вказівник this на об'єкт класу
T
має тип
T*
 Вказівник this завжди вважається константним і ніколи не передається статичним функціям- членам.
 Статичні змінні-члени мають особливі властивості. Вони доступні всім об'єктам класу, не належачи нікому з них окремо. Якби клас не був абстракцією, можна було б сказати, що статична змінна належить класу, а не об'єктам. У реальності це означає, що статична змінна-член класу існує в
єдиному екземплярі незалежно від кількості об'єктів. Особливо важно, що статична змінна
ініціалізується ще до створення першого об'єкта класу.
 Оголошення статичної змінної члену класу має декілька тонкощів. По-перше, мало оголосити статичну змінна-член — необхідно виділити для неї пам'ять. Для цього її оголошення варто повторити поза тілом класу, в області визначення глобальних змінних. Визначення статичної змінна- члена можна сполучити з її ініціалізацією. По-друге, повторювати ключове слово static при визначенні статичної функції не можна! Воно повинно бути присутнім тільки в тілі класу! Розглянемо приклад, у якому змінні
Re
і
Im оголошені статичними членами класу
TComplex
 Статичними можуть бути не тільки змінні, але і функції-члени класу. У цьому випадку вони втрачають доступ до нестатичних членів класу і можуть звертатися тільки до статичних змінна-членів
і функцій. Крім того, вони не можуть бути віртуальними, одержувати вказівник this
, а також не повинні мати кваліфікаторів const
і volatile
 Для захисту від модифікації члени класу, як правило, поміщають у закритий розділ і оголошують константними. Однак, якщо надалі нам знадобиться змінити константне поле, прийдеться виконувати приведення типу за допомогою оператора const_cast
. Утім, існує ще один спосіб захистити поля — розділивши функції-члени на функції доступу і функції, що модифікують. Перші функції не мають права змінювати вміст полів. Для цього них оголошують константними. Друга група функцій може вільно привласнювати полям нові значення.
 Якщо функція-член класу оголошена константно, вона не повинна змінювати зміст полів класу.
Однак якщо зняти захист з члену класу, поставивши перед його оголошенням ключове слово mutable
, то будь-яка константна функція зможе змінити значення цього поля.
 Мова С++ дозволяє створювати вкладенікласи. Це дозволяє сховати внутрішній клас від зовнішнього світу, обмеживши його область видимості зовнішнім класом.

Лекція
7
Класи
26
 Внутрішній (чи локальний) клас можна визначити не тільки усередині класу, але й усередині
функції, наприклад усередині функції main()
 Оскільки функції не можна визначати усередині інших функцій, за необхідності їхні визначення можна занурити усередину локального класу. Тільки під цією оболонкою вони доступні зовнішньої
функції.
 У силу обмеженості області видимості локальних класів, їм недоступні інші локальні змінні,
оголошені усередині функції, крім статичних і зовнішніх. Крім того, усередині локальних класів неможливо визначити статичні змінні.
 У деяких ситуаціях можна одержати доступ до функції-члена класу, не вказуючи імен ні об'єкта, ні
функції. Механізм, що дозволяє це зробити, ґрунтується на використанні вказівниківначленкласу.
Зрозуміло, щоб викликати функцію, необхідний реальний об'єкт класу, однак доступ до його функції- члена програма одержує за допомогою непрямої адресації. Для цього спочатку необхідно створити об'єкт у динамічній пам'яті й установити на нього вказівник. Потім з'являється вказівник на функцію- член класу. Тепер, передаючи як параметри вказівники на об'єкти і функції-члени, ми можемо викликати кожну функція-член класу, не називаючи ее явно. Природно, її оголошення повинне точно відповідати оголошенню вказівника на функцію-член.
 Прямий спосіб доступу до вказівника на член класу використовує об'єкт класу: z.*pMem
, а непрямий — посилання на нього: p->*pMem
. В обох випадках виробляється розіменування вказівника на член класу pMem
 На жаль, вказівники на члени класу не підкоряються правилам арифметики, прийнятим для звичайних вказівників. Саме з цієї причини нам приходиться ініціалізувати кожен член класу
TComplex окремою функцією — адже в нас немає засобів, що дозволяють переміщатися по членах класу шляхом інкрементації вказівника pMem
 У мові С++ для ініціалізації об'єктів призначений механізм, називаний конструктором. Це —
функція-член, ім'я якої збігається з ім'ям класу. Вона може мати будь-які параметри, що необхідні для
ініціалізації полів об'єкта. При цьому конструктор не має ніякого значення, що повертається. Його виклик являє собою визначення об'єкта. Інакше кажучи, визначаючи об'єкт, ви викликаєте конструктор.
 Конструктори можуть мати будь-які кількість чи параметрів не мати їх зовсім. Однак якщо конструктор має один параметр, виникає особлива ситуація: для нього існує три форми ініціалізації:
TComplex X(10); //
Перший варіант
TComplex Y = TComplex Y(10); //
Другий варіант
TComplex Z = 10; //
Третій варіант
 Найчастіше зручно задавати початкові значення за допомогою списку ініціалізації. Його синтаксична конструкція виглядає в такий спосіб.
ім
'
я
_
класу
(
тип параметр
1, ... тип параметр
N):
член
_
класу
(
параметр
1), ...,
член
_
класу
(
параметр
N) {}
Список ініціалізації — єдиний спосіб задати значення константних змінна-членів.
 Якщо в класі не визначений жодний конструктор, то об'єкт класу створюється за допомогою конструкторазазамовчуванням, або автоматичний конструктор, що, природно, не має параметрів.
Він заповнює всі поля глобального об'єкта нулями. Якщо об'єкт є локальним, його поля будуть містити випадкові значення. Усі функції-члени класу, створеного за допомогою конструктора за замовчуванням, працюють у відповідності зі своїм описом.
 Якщо програміст визначить у класі хоча б один конструктор, навіть без параметрів, компілятор не буде генерувати автоматичний конструктор.
 Знову створений об'єкт можна ініціалізувати змістом об'єкта, що уже існує. Для цього призначений конструкторкопіювання, що має наступний вид.
ім
'
я
_
класу
(
ім
'
я
_
класу
&)
 Конструктор копіювання викликається при ініціалізації об'єкта під час оголошення., при передачі
об'єкта як параметр функції і при поверненні об'єкта як значення, що повертається.
 Глибокекопіювання означає створення абсолютно самостійної копії об’єкта, що раніше існував раніше існував об'єкта (з іншою адресою, але тим же змістом).
 Якщо програміст не визначив конструктор копіювання, то буде викликаний конструктор копіюваннязазамовчуванням. Він виконує побітове, тобто поверхневекопіювання, і замість нової
копії ми одержимо два вказівники, що посилаються на той самий адресу.
 Конструктори, як і звичайні функції, можна перевантажувати, варіюючи типи і кількість параметрів.
Однак бувають ситуації, коли об'єкт класу повинний створюватися двома різними способами, що реалізуються конструкторами, що мають однакову кількість і тип параметрів. У уьому випадку використовується ідіома «іменованих конструкторів», що складається із статичних функцій-членів,
що викликають закритий конструктор і повертають створений об'єкт класу. Ідея цієї ідіоми полягає в

Лекція
7
Класи
27
тім, що статичні функції-члени викликаються ще до створення об'єкта зазначеного класу. Отже, з
їхньою допомогою можна викликати закритий конструктор, передавши йому відповідний набір аргументів.
 Відзначимо два моменти: 1) конструктор оголошений закритим, тому доступ до нього можливий лише через функції інтерфейсу; 2) інтерфейсні функції є статичними, тому їх можна викликати до створення нового об'єкта. Якби функції не були статичними, їх виклик став би можливий тільки після створення об'єкта. Однак оскільки конструктор оголошений закритим, створити об'єкт було б неможливо. Ключове слово static розриває це замкнуте коло.
 Деструктор знищує об'єкти класу, звільняючи зайняті ресурси. Ця функція, як і конструктор, не має
значення, що повертається, однак, у відмінність він конструктора, вона не має одного параметра. Ім'я деструктора повинне збігатися з ім'ям класу, перед яким поставлена тильда.

ім
я
_
класу
() {
тіло деструктора
}
 Особливістю деструктора є той факт, що він ніколи (за одним-єдиним виключенням) не викликається явно. Просто, коли об'єкт виходить з області видимості, програма сама викликає
деструктор, що виконує запропоновані дії. Якщо програміст не визначив деструктор класу,
компілятор згенерує деструкторзазамовчуванням.
 Синтаксисрозміщення — це різновид оператора new
, що дозволяє створювати і розміщати об'єкт по заздалегідь заданій адресі. Спочатку ми резервуємо пам'ять для об'єкта , а потім створюємо об'єкт за допомогою синтаксису розміщення. Саме тому, що ми занадто глибоко втрутилися в механізм розподілу пам'яті для нових об'єктів, нам доведеться самим подбати про її звільнення, явно викликавши деструктор.
 Для того щобфункція мала прямий доступ до полів класу, її можна оголосити функцію дружньої,
використовуючи ключове слово friend
 Дружня функція має всі права члена класу. Для неї відкриті всі поля — як відкриті, так і закриті. Для того щоб оголосити функцію дружньої, ключове слово friend слід помістити в оголошення класу.
(Клас сам оголошує своїх друзів!)
 Працюючи з дружніми функціями, потрібно враховувати наступні обмеження: вони не успадковуються, вони не можуть мати специфікатори static
і extern
; у класі, функція-член якого
є дружньої до не цілком оголошеного класу, слід розміщати лише прототип. Реалізація функції
повинна знаходитися після повного оголошення класу.
 Коли усі функції деякого класу є дружніми стосовно іншого класу, можна оголосити весь клас дружнім.
 Що таке неповне оголошення класів?
 Іноді два класи настільки тісно зв'язані, що їм необхідний повний доступ до всіх членів. У цьому випадку їх оголошують взаємно дружніми.
7.5.
КОНТРОЛЬНІ
ПИТАННЯ
1. Які поняття і принципи покладено в основу об'єктно-орієнтованого програмування?
2. У чому полягає основна ідея об'єктно-орієнтованого програмування полягає?
3. Сформулюйте принцип інкапсуляції.
4. Опишіть сутність поліморфізму.
5. Що таке клас і об’єкт?
6. Як описується клас у мові С++?
7. З яких розділів складається тіло класу?
8. Що означає ключове слово public
?
9. Що означає ключове слово private
?
10. Опишіть об’єднання та обмеження, що на них накладаються.
11. Як діють специфікатори доступу?
12. Які функції-члени називаються функціями-членами доступу?
13. Які функції-члени називаютьсяфункціями-членами, щомодифікують?
14. З яких функцій складається інтерфейс класу?
15. Як звернутися до членів класу?
16. Які ключові слова не можна використовувати при оголошенні полів класу?
17. Що являє собой вказівник this
?
18. Опишіть особливості статичних змінних-членів.
19. Опишіть особливості статичних функцій-членів.
20. Як зняти захист з члену класу?
21. Як оголосити вкладені класи? Опишіть особливості.
22. Як використовується вказівник на функцію-член?
23. Опишіть прямий і непрямий способи доступу до вказівника на член класу.
24. Що таке конструктор?

Лекція
7
Класи
28
25. Що таке список ініціалізації?
26. Що таке конструкторазазамовчуванням, або автоматичний конструктор? У яких ситуаціях від генерується, а у яких ні?
27. Опишіть процес ініціалізації об’єкта.
28. У яких ситуаціях викликається конструктор копіювання?
29. Що собою являє глибокекопіювання?
30. Яке копіювання здійснює конструктор копіювання за замовчуванням?
31. Опишіть ідіому «іменованих конструкторів».
32. Що таке деструктор? Які особливості його виклику?
33. Що таке синтаксисрозміщення?
34. Які функції називаються дружньоми відносно класу.
35. Які обмеження накладаються на використання дружніх функцій?
36. Які класи називаються дружніми?
37. Які класи називаються взаємно дружніми?

1   2   3   4

скачати

© Усі права захищені
написати до нас