You are on page 1of 43

Лекція 1

Лекція 1
Історія мови С++. Основні поняття.
 
С++, очевидно, найбільш вдала з мови С. Мова С походить від BCPL. До речі, коментарі типу //
були введені в С++ з  BCPL. Другим джерелом походження була мова Simula 67. З неї взято
поняття класу, похідного класу і віртуальних функцій. Такі можливості мови С++, як
перевантажувати ( перевизначати ) оператори  і забезпечувати вільне розміщення декларацій в
будь-якому місці, де зустрічається відповідний оператор нагадують мову Алгол 68.
Сама назва С++ з’явилася в 1983 р. Більш ранні версії цієї мови використовувались з 1980р. З
загальною назвою „Сі з класами”. Проте в мові „Сі з класами” не було перевантаження
операторів, посилань, віртуальних функцій та інших деталей. Вперше С++ з’явився літом 1983р.
На цей час вже були придумані практично всі можливості сучасної мови С++.
Назву С++ придумав Rick Mascitti. Ця назва відображає еволюційний характер змін мови С. „++” –
оператор інкрементації в мові С. С++ це розширення мови С.
Початкова мета створення С++ - дати можливість не програмувати на Асемблері, С або інших
мовах високого рівня. Основна мета – допомогти програмістам легше і приємніше створювати
нові, добрі програми.
В якості базової мови для С++ було обрано мову С, оскільки вона:
- гнучка, компактна і має відносно низький рівень;
- підходить для програмування більшості системних задач;
- працює всюди і на всьому;
- добре узгоджується з середовищем UNIX.
 
Чим краще Ви знаєте мову С, тим важче Вам буде писати на С++, не використовуючи стиль
програмування на С, і відповідно, не гублячи деякі переваги С++. Досвід показує, що для
вивчення С++ не слід спочатку вивчати мову С. Мова С основана на концепції структурного
програмування, а С++ - повністю ООП.
 
С++ в наш час рахується пануючою мовою для розробки комерційних програмних продуктів. Хоча
в останні роки це панування підірвалось появою нової мови програмування Java. Проте більшість
програмістів, що спочатку перейшли на Джаву, потім поспішили знов до С++. Ці дві мови дуже
подібні, тому вивчивши одну з них, ви автоматично оволодіваєте 90% іншою.
 
Міжнародний стандарт мови С++ був створений Акредитованим комітетом стандартів, що діє під
керівництвом Американського національного інституту стандартів ANSI.
 
 
Відмінності С++ від С.

1. Розширення: можна описувати типи аргументів функцій; при цьому відповідність типів буде
перевірятися. Виконуються перетворення типів.
2. Для виразів типу float можна використати арифметику з плаваючою крапкою одинарної
точності.
3. Імена функцій можна суміщати.
4. Оператори можна перевантажувати.
5. Щоб запобігти втратам при викликах функцій застосовується оператор inline, який
дозволяє вбудовувати тіло функції замість її виклику.
6. Не треба використовувати функцію malloc( ), оператор new робить те саме краще.
7. Макроси типу #define практично необов’язкові в С++. Для задання явних констант
використовується const.
8. Ім’я класу є іменем типу.

 
Огляд несумісностей:
Більшість конструкцій мови С дозволені в С++. Їх значення не змінюються. Винятками є наступні
конструкції:
-         не дозволені програми, що використовують одне з нових ключових слів в якості
ідентифікаторів.
Class, const, delete, friend, inline, new, operator, overload, public, signed, this, virtual, volatile.
- оголошення f( ) означає, що функція не використовує аргументи. В С це означає, що функція
може використовувати аргументи будь-якого типу.
- в С зовнішнє ім’я можна оголошувати декілька разів, в С++ воно повинно бути оголошене лише
1 раз.
- імена класів в С++ займають той ж адресний простір, що і інші імена, тобто давати однакові
імена змінним, функціям, структурам не можна.
 
Суть об’єктно-орієнтованого програмування у використанні концепції об’єктів, які мають
характеристики та можливості. Задача ООП – правильно представити об’єкти на мові
програмування.
ОПП – методологія програмування, яка базується на представленні програми у вигляді сукупності
об’єктів, кожен з яких є реалізацією певного класу, а класи утворюють ієрархію на
принципах наслідування.
В цьому означенні можна виділити три частини:
1) ОПП використовує в якості елементів конструкції об’єкти, а не алгоритми.
2) кожен об’єкт є реалізацією якогось певного класу.
3) класи організовані ієрархічно.
 
Взагалі розрізняють 5 основних різновидів стиля програмування:
1) процедурно-орієнтований (алгоритми);
2) об’єктно-орієнтований (класи і об’єкти);
3) логічно-орієнтований (цілі, що виражаються предикатами);
4) орієнтований на правила (правила „якщо ... то”);
5) орієнтований на обмеження (інваріантні співвідношення).
 
Для ООП властиві 4 основних і 3 додаткові елементи:
Основні:
1) абстрагування;
2) обмеження доступу;
3) модульність;
4) ієрархія.
Додаткові:
1) типізація;
2) паралелізм;
3) стійкість.
 
Абстрагування – виділення таких суттєвих характеристик деякого об’єкту, які вирізняють його
серед всіх інших видів об’єктів і таким чином, чітко визначають особливості даного об’єкту з точки
зору подальшого розгляду і аналізу.
Обмеження доступу – процес захисту окремих елементів об’єкту, що не торкається суттєвих
характеристик об’єкту як цілого.
Модульність – властивість системи, що пов’язана з можливістю декомпозиції її на ряд тісно
пов’язаних модулів. Розподіл програми на фрагменти дозволяє частково зменшити її складність.
Ієрархія – впорядкована система абстракцій. Приклади ієрархії: - просте наслідування;
- множинне наслідування; - агрегування.
Типізація – обмеження, що накладається на клас об’єктів і запобігає взаємозаміні різних класів.
Паралелізм – це є можливість одночасного функціонування об’єктів. Це властивість об’єктів
знаходитись в активному або в пасивному стані.
Стійкість – властивість об’єкту існувати в часі (без залежності від процесу, що його породив) і
(або) в просторі (переміщення об’єкту з адресного простору, де він був створений).
 
Переваги ООП:
-         використання виразних засобів ОО програмних мов;
-         підтримка повторного використання окремих складових програмного забезпечення;
-         створення більш відкритих систем;
-         зниження ризику при розробці;
-         активізація пізнавальних здібностей людини.
 
Недоліки ООП  можуть вплинути на
-         характеристики системи (швидкодію);
-         початкові затрати (навчити).
 
 
 
 
В мові С++ повністю підтримуються принципи ООП, а
саме: інкапсуляція, наслідування та поліморфізм.
 
Інкапсуляція
Це є властивість прихованості або автономності об’єкту, які використовуються у зовнішніх
конструкціях. З допомогою інкапсуляції можна забезпечити прихованість даних. Користувач
використовує об’єкт не знаючи його внутрішньої роботи.
Приклад: для ефективного використання резистора інженеру зовсім не обов’язково знати
принципи його роботи та внутрішню будову. Важливо, щоб він виконував свої функції.
В мові С++ інкапсуляція підтримується через розробку нестандартних типів даних, які
називаються класами. Грамотно визначений клас працює як повністю інкапсульований об’єкт,
його можна використовувати як суцільний програмний модуль. Справжня внутрішня робота класу
повинна бути прихована. Користувачу необхідно лише знати, як цей клас використовувати.
 
Наслідування.
Це означає, що можна об’явити новий тип даних (клас) , що є розширенням існуючого класу. Цей
новий клас є нащадком існуючого класу, і називають його похідним.
 
Поліморфізм
Мова С++ дозволяє вносити зміни у виконання однойменних функцій для різних об’єктів
через поліморфізм функцій і класів. Поліморфізм – це багато форм.
 
Підготовка до програмування
Мова С++ потребує від програміста попереднього проектування програми на етапі , що передує
написанню кода.
Перше питання при цьому звучить приблизно так: Яку проблему я хочу вирішити? Кожна
програма має мати чітко сформульовану ціль.
Друге питання: чи можливо вирішити цю проблему існуючими програмними засобами? Можливо,
достатньо скористатись старою програмою, чи купити у когось готову програму? Часто таке
рішення може бути кращим, ніж створювати нову програму. Програміст. Що пропонує таку
альтернативу ніколи не залишиться без роботи: вміння знаходити економні рішення проблеми
забезпечать йому популярність в майбутньому.
 
Етапи розробки програми на мові С++ передбачають створення кода програми (файл з
розширенням .срр), компіляцію (.obj) та компоновку (.ехе).

Лекція 2
Лекція 2
Об’єктно-орієнтоване програмування.
 
Мова С++ була створена як об’єктно-орієнтоване продовження одної з самих розповсюджених
мов програмування для розробки комерційних програм мови С. Об’єктно-орієнтовані мови
програмування забезпечують потужний зв’язок між структурами даних та методами, з допомогою
яких цими даними маніпулюють. Найбільшою перевагою ООП є те, що програміст більше не
зобов’язаний піклуватись про структури даних та керуючі функції. Все, що необхідно – це
правильно використовувати створений об’єкт.
Світ наповнений об’єктами: автомобілі, собаки, дерева, квіти – все це об’єкти. Кожний об’єкт має
свої властивості (швидкий, пухнастий, зелене, запашні). Об’єкти поводять себе певним чином
(їдуть, лають, ростуть, розквітають).
Нагадаємо, що тип змінної визначає:
-         її розмір в пам’яті;
-         тип інформації, яку вона може зберігати;
-         операції, які можуть з нею виконуватись.
В мові С++ програміст може сам розширити можливості мови, створюючи власний тип даних, що
необхідний для розв’язку текучої задачі. Кожний зі створених типів має всі функційні можливості
та права вбудованих типів.
Можна вирішити задачу з допомогою тільки типу цілого чи символу, та для розв’язку складної
задачі простіше мати можливість описати об’єкт, про який йде мова. Наприклад, змоделювати
роботу системи опалення через змінні, що описують датчики температури, термостати та
бойлери.
 
Об’єкт
Стан об’єкту характеризується переліком всіх можливих властивостей (статичних) і поточними
значеннями (динамічними) кожної з цих властивостей.
Поведінка характеризує те, як об’єкт впливає або підлягає впливу інших об’єктів з точки зору
зміни стану цих об’єктів і передачі повідомлень. Поведінка об’єкту повністю визначається його
діями.
В поведінці об’єкту виділяється поняття операції. З практики відомо 5 основних видів операцій
над об’єктами:
1) модифікатор – операція, що змінює стан об’єкту шляхом запису або доступу.
2) селектор – операція, що дає доступ для визначення стану об’єкту без його зміни (операція
читання).
3) літератор – операція доступу до вмісту об’єкту по частинах ( в певній послідовності).
4) конструктор – операція створення і (або) ініціалізації об’єкту.
5) деструктор – операція знищення об’єкту і (або) звільнення пам’яті, що він займав.
 
Стосовно поведінки об’єкти можуть бути активними і пасивними. Активні об’єкти можуть
реалізувати свою поведінку без впливу з боку інших об’єктів. Активні об’єкти розміщаються в так
званому каналі управління. Пасивні об’єкти можуть змінювати свій стан лише під впливом інших
об’єктів.
Якщо система має декілька каналів управління, то і активних об’єктів може бути багато. В
послідовних системах в кожний момент часу існує лише 1 активний об’єкт (і відповідно лише один
канал управління).
 
Індивідуальність – такі властивості об’єкта, які вирізняють його серед всіх інших об’єктів.
Наприклад, ім’я об’єкту завжди залишиться тим самим, які б значення змінним ми не
присвоювали. Це нагадує древньо філософське питання: чи залишиться річка сама собою
процесі часу, хоча ніколи вже попередня вода не потече в ній.
Розрізняють операції присвоювання і рівності об’єктів, а також час існування об’єктів.
 
Відношення між об’єктами

1. 1.     Відношення використання. 

Лінії між двома умовними об’єктами  означають присутність відношень використання між ними і
має на увазі можливість передачі повідомлень цією лінією. Таке пересилання повідомлень може
бути одно- і двонаправленим.
Кожен об’єкт, включений у відношення використання може виконувати наступні 3 ролі:
-         вплив: об’єкт може впливати на інші об’єкти, але сам ніколи не підлягає впливу інших
об’єктів;
-         виконання: об’єкт може лише підлягати керуванню з боку інших об’єктів, але ніколи не
виступає в ролі об’єкту, що мають вплив;
-         посередництво: об’єкт може виступати як впливаючим, так і виконавцем. Об’єкт-
посередник створюється, як правило, для виконання певних операцій в інтересах певного
активного об’єкту або іншого об’єкту-посередника.
Поняття синхронізації: при передачі повідомлень від одного об’єкту до іншого, обидва об’єкти
при взаємодії повинні синхронізуватися певним чином. Для послідовних систем така
синхронізація реалізується через виклики підпрограм. Але в паралельних системах ситуація
значно складніша, оскільки необхідно вирішувати проблему виняткових ситуацій. Звідси випливає
ще один спосіб класифікації об’єктів:
-         об’єкт-транслятор: пасивний об’єкт, що має лише один канал управління;
-         блокований об’єкт: пасивний об’єкт, що має декілька каналів управління;
-         паралельний об’єкт: активний об’єкт, що має декілька каналів управління.
 

1. 2.     Відношення включення.

Об’єкти „Мотор” та „Датчик” є елементами стану об’єкту „Ліфт”.


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

Лекція 3
Лекція 3
 
Об’єктно-орієнтований аналіз та проектування
 
1.Побудова моделей
Задачею моделі є абстрактний опис всіх об’єктів реального світу та їх
взаємозв’язок. Абстракція завжди простіше реальності, але точно
відобразить всі її різноманітні сторони. Глобус є класична модель земної
кулі. Модель – це не сам предмет, але по ньому можна багато дізнатись
про планету Земля. Мистецтво об’єктно-орієнтованого
програмування полягає у створенні  доброї моделі об’єкту, що
розглядається. Основою моделі є мова моделювання та сам процес.
Мова моделювання – це домовленість, яка прийнята для того, щоб
мати можливість намалювати на папері модель, що розглядається.
Загальноприйнятою мовою в розробці програмного забезпечення є UML
– уніфікована мова моделювання. В UML класи зображуються у вигляді
прямокутників, а наслідування – у вигляді стрілки, яка направлена від
спеціалізованого класу до базового, основного класу.
Процес аналізу та проектування на основі об’єктно-орієнтованого
підходу набагато складніший та важливіший ніж мова моделювання.
Метод – це і мова моделювання і процес. Трьома ведучими
розробниками методів є: Грейди Буч, який розробив метод
Буча; Ивар Якобсон, розробив метод програмування на основі об’єктно-
орієнтованого (ОО) підходу; Джеймс Рамбо, що розробив технологію
об’єктного моделювання ОМТ. Разом вони
створили Rational UnifiedProcess - метод та комерційний продукт
виробництва Rational Software.
Розрізняють два види процеса проектування: каскадний або послідовний
та циклічний або спіральний. Циклічний процес означає, що при розробці
програми необхідно періодично повторювати весь процес, в ході
поглиблення розуміння задачі. На окремих етапах рішення задачі
відбувається повернення до початку, постійне вдосконалення
інтерфейсів та реалізацію процедур певних об’єктів. При
циклічному підході до проектування можна починати, маючи лише
загальну концепцію того, що має бути в результаті. В ході реалізації
виникають все нові та нові нюанси.
Каскадний процес проектування передбачає таку послідовність
проектування, при якій вихід з однієї стадії стає входом в наступну.
Каскадний метод включає початкове визначення всіх деталей, вимог до
готового продукту. І після узгодження з клієнтом вимоги передаються
розробнику. Розробник створює проект та передає його програмісту на
реалізацію. Програміст в свою чергу передає створений
код тестувальнику, який перевіривши код передає його замовнику.
Отже каскадний та циклічний методи включають однакові етапи
розробки, які різняться послідовністю виконання, а саме:
-         концептуалізація – це основна ідея проекту;
-         аналіз вимог– це процес зрозуміння вимог;
-         проектування – створення моделей всіх класів;
-         реалізація – написання коду програми;
-         перевірка або тестування– гарантує роботоздатність програми;
-         відлагодження – нанесення останніх поправок перед здачею замовнику;
- супровід.
 На першій стадії аналізу та проектування ОО програмного
продукту ідея (концепція) повинна бути зафіксована одним реченням.
Ідея стає ведучим принципом розробки, яка можливо в ході розробки
буде коректуватись. Ідея визначає вимоги до проекту. Деталі можуть
бути скоректовані, але основна задача повинна залишитись незмінною.
Одним з сучасних підходів в аналізі та проектуванні є екстремальне
програмування (ЕП). Ця методологія розробки програмного
забезпечення є найпопулярнішою серед так
званих гнучких методологій. Має на метіполіпшення якості програмного з
абезпечення та чутливість до змін у вимогах замовників. Як
вид гнучкихметодологій, ЕП «радить» часті «випуски» програми у
коротких
циклах розробки, що має на меті поліпшитипродуктивність праці та покра
щити можливості виконання вимог замовника що змінюються.
Авторами даноїметодології є Кент
Бек, Ворд Каннінгем, Мартін Фаулер та інші.
Інші елементи екстремального програмування включають в собі: парне п
рограмування, проведення обширноїперевірки сирцевого коду, модульне 
тестування всього коду, уникання створення функціональності до того як
вона дійсно необхідна, простота
та ясність коду, очікування на зміну вимог замовників з плином часу та
коливимоги до
продукту стають ясніші, досить часте спілкування із замовником та між с
амими програмістами. Назваметодології походить від ідеї застосувати ко
рисні методи і
практики розробки програмного забезпечення,піднявши їх до
«екстремальних» рівнів.
 
Аналіз вимог
 
Щоб перейти до аналізу, треба зрозуміти, як, де і ким буде використаний
даний програмний продукт. Мета аналізу полягає в тому, щоб
сформулювати та зафіксувати ці вимоги. Результатом аналізу має бути
документ з чіткими вимогами до розробників проекту. Першим розділом
цього документу є аналіз прецеденту.
Прецедент – це детальний опис способів застосування програми.
Створення повного та точного переліку прецедентів може виявитись
єдиною та найбільш важливою задачею аналізу. Тут все залежить від
кваліфікаціїведучого системного спеціаліста, саме він володіє
інформацією про комерційні характеристики та інші вимоги, які необхідно
реалізувати у проекті. Основну увагу в прецедентах слід віддати
користувацькому інтерфейсу. Будь-яка система чи людина, що
спілкуються з системою називаються виконавцем. Прецедент – це опис
взаємодії між виконавцем та системою. При аналізі прецедента система
розглядається як „чорна скринька”. Виконавець посилає повідомлення
системі, а вона на нього реагує: видає інформацію,  змінює стан,
поведінку.
 
Визначення виконавців
 
Як вже було сказано, не всі виконавці є людьми. Інші системи, які
взаємодіють з новою системою, також є виконавцем. Основні
характеристики виконавців:
-         вони є зовнішніми по відношенню до системи;
-         вони взаємодіють з системою.
 
Створення моделі сфери діяльності
 
Визначивши прецеденти, необхідно перейти до детального
документування вимог, включаючи модель сфери діяльності. Це є
документ, який охоплює все що відомо про сферу діяльності, що
розглядається. Частиною сфери діяльності є об’єкти, що створюються і
які описують реальні об’єкти, що згадуються у прецедентах. Для кожного
такого об’єкту необхідно зафіксувати найбільш суттєві данні, такі як ім’я 
об’єкту, чи є об’єкт виконавцем, його основні атрибути, поведінка, тощо.
Тут ідеться мова про реальні об’єкти в реальній сфері діяльності. Це є
документування процесів реального світу, а не того, як буде працювати
нова система.
Використовуючи засоби UML для класів та зв’язків, можна встановити,
що текучий та депозитний рахунки є  похідними від банківського рахунку.
На малюнку прямокутниками представлені об’єкти, стрілками показано
узагальнення. В UML стрілка вказує від спеціалізованого класу до
загального базового.
Основними зв’язками, що документуються при аналізі, є узагальнення
( або наслідування), входження та асоціація.
 
Узагальнення
 
Між узагальненням та наслідуванням є принципова різниця.
Узагальнення описує взаємозв’язки, а наслідування є реалізацією
узагальнення на програмному рівні. Спеціалізація це протилежне
узагальненню. Кіт є спеціалізованою формою тварин, а тварини –
узагальнена форма кота, собаки. Спеціалізація передбачає, що похідний
об’єкт є підтипом базового об’єкту. Взаємозв’язок симетричний:
банківський рахунок узагальнює основні атрибути та поведінки текучого
та депозитного рахунків.
По ходу аналізу сфери діяльності необхідно зафіксувати ці взаємозв’язки
в такому вигляді, як вони існують в реальному світі.
 
Входження
 
Інколи об’єкт складається з декількох інших об’єктів. Текучий рахунок
складається з балансу, списку трансакцій, ідентифікаційного коду та
інше. Кажуть, що ці елементи входять в текучий рахунок. Для моделі
входження є різновид взаємозв’язку. В UML взаємозв’язок
представлений як стрілка з ромбиком, що направлена від об’єкту
довмістимого. Наприклад,

Ми утворили схему взаємозв’язку об’єктів засобами UML.


 

Асоціація
 
Асоціація передбачає, що два об’єкти взаємодіють між собою. Для
аналізу достатньо знати, що об’єкти А і Б взаємодіють, але ні один з них
не містить та не походить від іншого. В UML асоціація  позначається
прямою лінією між об’єктами. 
Визначення сценаріїв
 
Перейдемо до побудови схеми зв’язків об’єктів у сфері діяльності.
Кожний прецедент можна розбити на ряд сценаріїв. Сценарій – це опис
специфічного набору обставин, які можуть вплинути на розвиток подій
прецеденту.  Кожний сценарій розглядає один з варіантів розвитку подій
прецеденту. Необов’язково розглядати всі можливі сценарії. Достатньо
визначити лише ті, які можуть призвести до ситуацій, не передбачених у
вимогах до системи.
Приклад сценарію:
-         клієнт бажає зняти 300 $ з текучого рахунку, забирає готівку з автомату,
система друкує квитанцію.
 
Визначення критеріїв
 
У склад прийнятої методики документування сценаріїв, входить набір
критеріїв. Ці критерії необхідно відобразити у документі вимог. Основний
набір критеріїв наступний:
- при яких умовах можливий початок сценарію (початкові умови)?
- яка причина початку сценарію (тригер)?
- що роблять виконавці?
- які результати, які зміни стану системи?
- який результат для виконавця?
- чи повторюються дії, якщо так, то які умови їх завершення?
- опис логіки сценарію?
- які умови завершення сценарію?
- при яких умовах можливе позитивне завершення сценарію (умови
виходу)?
В документі вимог необхідно вказати прецедент та ім’я сценарію
наступним чином:
Прецедент:  ...
Сценарій: ...
Початкові умови: ...
Тригер: ...
Опис: ...
Умови виходу: ...
 
Діаграми взаємодій
 
При об’єднанні схем прецедентів можна суттєво покращити
документацію і зробити взаємодію більш зрозумілою. Формується
діаграма взаємодії, яка дозволяє виявити такі подробиці сценарію, які не
завжди можна побачити при читанні тексту. Приклад діаграми взаємодії
засобами UML:

Оскільки для кожної задачі створюється ряд прецедентів різної


складності, UML дозволяє об’єднати їх в пакети.
Пакет подібний каталогу або папці, це є набір за певною
характеристикою об’єктів, що моделюються. Один прецедент може бути
присутнім у різних пакетах.
Окрім прецедентів, документ вимог включає передбачення замовника 
проекту, умови, обмеження, вимоги до апаратних засобів та операційної
системи. Вимоги до проекту – це саме те, що хоче конкретний замовник.
Якщо проект передбачає взаємодію з існуючими системами, то
необхідно розібратись з цими системами, як вони працюють. Необхідно
вирішити яка система буде сервером, а яка клієнтом. Чи прийдеться
налагоджувати нову систему під існуючий стандарт? Як довго буде
використовуватись нова система? Обов’язково необхідно зафіксувати
умови та обмеження, які можуть виникнути при взаємодії з іншими
системами.
Переважно замовник вже має певну операційну систему та апаратну
платформу і хоче щоб нова система працювала саме під його ОП. Це
необхідно врахувати у вимогах.
Складаючи бюджет та графік дотримуйтесь наступних принципів:
-         враховуйте можливість переросходу фондів;
-         враховуйте можливість переросходу часу.
Тому роботу необхідно розподілити за пріоритетом. Все і вчасно
закінчити не вдасться, треба бути до цього готовим. Головне, щоб
вчасно був готовий працездатний екземпляр програми на рівні першої
версії. Точний час реалізації стане відомий тільки на етапі тестування.
Крім того бажано зарезервувати 20-25% часу на всяк випадок, якщо по
ходу проекту виникнуть непередбачені ситуації.
 
Візуалізація
 
Візуалізація є заключною частиною документу вимог. Це є діаграми,
малюнки, зображення екранів, прототипи та інше, що допомагає при
проектуванні графічного інтерфейсу користувача нового проекту.
Графічна частина дозволяє наочно побачити, якою має бути система і як
вона має працювати в кінцевому варіанті.
 
Артефакти
 
В кінці кожного етапу аналізу та проектування накопичується ряд
документів, які називаються артефактами. Ці документи
використовуються для організації взаємодії та протоколювання відносим
між замовником та виконавцем. Їх підписання обома сторонами гарантує,
що виконавець чітко зрозумів та врахував всі вимоги замовника та
зобов’язується розробити у вказані терміни проект.
Приклад документів – артефактів:
Опис прецедентів; аналіз сфери діяльності; аналіз схем взаємозв’язків;
аналіз схем взаємодій; аналіз системи; вимоги до проекту; список умов
та обмежень; кошторис та календарний план.
 
 
Проектування
 
Проектування – це процес перетворення задач проекту у модель, яку
можна реалізувати у вигляді програмного забезпечення. Результатом
процесу проектування є створення документу – проекту. Документ
проекту складається з двох частин: проекту класів та архітектури
системи. Проект класів включає опис різних класів, їх структури та
характеристик, опис взаємодії цих класів. Розділ архітектури системи
включає детальний опис реалізації об’єктів, їх взаємодію, загальну
систему об’єктів.
В програмах на мові С++ використовуються класи. Класи, що
створюються в коді програми на мові С++, є реалізацією класів, що
створені на етапі проектування. Класи ізоморфні: кожному класу проекту
відповідає клас в програмному коді, але не слід їх плутати. Схеми UML
представляють класи моделі, а класи мови С++ описані у коді, що
компілюється.
Найбільшу складність викликає визначення вихідного набору класів та
правильне їх створення. Спрощена методика пропонує спочатку
записати сценарії прецедентів, а потім для кожного об’єкту створити
свій клас. Наприклад, розглянемо наступний сценарій прецеденту:
Клієнт збирається зняти готівку з текучого
рахунку. Система заправлена папером
для квитанції, мережаприєднана. Банкомат питається
про суму готівки. Машина видає потрібну суму та друкує квитанцію.
Клієнт забирає гроші.
Виходячи з цього сценарію можна утворити наступні класи: клієнт,
готівка, текучий рахунок, система, квитанція, мережа, сума, машина,
гроші.
Після об’єднання взаємопов’язаних об’єктів отримуємо: клієнт, готівка
(гроші, сума), текучий рахунок, квитанція, машина (система), мережа. Це
і є новостворені класи.
Класи створюються і для інших дій: інтерфейс між новою та існуючою
системами; інтерфейс для взаємодії з базою даних; клас, що перетворює
дані; клас для підготовки звітів та збору інформації; клас пристрою для
відображення взаємодії системи з зовнішнім пристроєм.
 
Наступним кроком утворюємо статичну модель. Вона охоплює три
області: розподіл відповідальності, атрибути та взаємодії. Найбільш
важлива з них – розподіл відповідальності між класами. Кожний клас має
виконувати конкретну задачу, яка реалізується одним або багатьма
методами.
 
Взаємозв’язок класів
 
Види взаємозв’язків: узагальнення, асоціація, агрегація, композиція.
Взаємозв’язок узагальнення реалізований у С++ через
відкрите наслідування. Тут мається на увазі необхідність розглянути
зовнішні фактори функціонування взаємозв’язаних класів. Якщо два
похідних класи мають однакові функції, то має сенс передати ці функції
наверх, в узагальнюючий клас, базовий. Чим більше факторів буде
переведено з похідного класу в базовий, тим більш поліморфним буде
проект.
Однією з переваг С++ над Java, є   множинне наслідування, яке
дозволяє класу наслідувати властивості більш ніж одного
базового класу, присвоюючи собі всі члени та методи всіх
базових класів. Користатися множиннимнаслідуванням треба обережно,
оскільки воно може ускладнити реалізацію проекту.
В моделі відкрите наслідування завжди є узагальненням.
Загальноприйнятим виразом для опису наслідувальних взаємозв’язків
при моделюванні є термін „мати” („володіти”). Взаємозв’язок типу мати
моделюється з допомогою агрегації або входження або вкладеність.
Наприклад, автомобіль має кермо, від 2 до 5 дверей, 4 колеса. Агрегація
буде мати наступний вигляд:

В агрегації всі складові об’єкти можуть існувати незалежно від


вихідного класу. В композиції навпаки: клас не тільки включає в себе
складові об’єкти, але вони будуть створені та існувати тільки разом
з класом, вони не мають незалежного існування.
 
Не менш важливою є модель взаємодії класів. Існує два різновиди схем
взаємодії. Схема послідовності дій це є послідовність подій за
певний проміжок часу; схема взаємодій представляє взаємодії
між класами і ця схема може бути безпосередньо створена з схеми
послідовності дій. Ця задача може бути автоматизована
засобамиRational Rose.
 
Тепер змоделюємо стани кожного  об’єкту. Перехід об’єкту з одного
стану в інший можна з допомогою схеми станів. Кожна схема станів
починається з вихідного стану, що позначено як „Початок”, та
завершується будь-якою кількістю кінцевих станів. Всі стани мають
назви, „захист” означає умову, яка має виконуватись для об’єкту, щоб він
міг перейти з одного стану в інший.
 Резюме

Отже, ми розглянули аналіз та проектування ОО програм. Аналіз


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

Лекція 4
Лекція 4
Функції в С++
 
Що таке функція? Це підпрограма, яка може оперувати даними та повертати значення. Кожна
програма на мові С++ містить хоча б одну функцію – main(), яка при завантаженні програми
викликається автоматично. Ця функція може викликати інші функції, які не є частиною об’єкту. До
них можна звернутись з будь-якого місця програми, тому вони називаються глобальними. Про
них і піде мова. Виклик функції відбувається за її ім’ям. По завершенню роботи функції виконання
програми відновлюється зі стрічки, що записана після виклику функції.
Функції класифікують на два види: користувальні та вбудовані. Користувальні створюються
програмістом підчас написання конкретної програми, а вбудовані належать до пакету
компілятора.
Результатом роботи функції є значення тип якого повідомляється при оголошенні функції
 
int myf();
 
Опис значень, які передаються функції називається списком параметрів. Для кожного параметра
має бути описаний його тип.
 
int myf( int n, float m);
 
Фактичні значення, що передаються функції, називаються аргументами.
 
int P = myf (5, 6.7);
 
Тип аргументів має співпадати з типом параметрів. Оголошення функції разом з параметрами
називається прототипом.
Існує три способи оголошення функції:
-         записати прототип функції у файл, а потім використати директиву #include , щоб включити
файл в текст програми;
-         записати прототип в той файл, де ця функція застосовується;
-         функція має бути визначена до того, як її викличить інша функція. В цьому випадку
визначення виступає в якості оголошення.
Таким чином прототип складається з типу значення, що повертає функція, імені функції та списку
параметрів, що включає тип параметра та його Ім’я. Прототип закінчується ;
 
int myf( int n, float m);
 
Визначення функції складається із заголовка функції та її тіла. Заголовок аналогічен прототипу
але в кінці не ставлять ;. Тіло  функції складається з групи операторів, що береться у фігурні
дужки.
 
int myf( int n, float m)
{
  ...
return (...);
}
 
Після виклику функції управління передається першому оператору тіла функції. Функції можуть
викликати інші функції та саму себе.
 
У функції можна використовувати не тільки значення, що передають аргументи, але оголошувати
власні змінні всередині тіла функції, які називаються локальними. При поверненні з функції
локальні змінні видаляються з пам’яті. До змінних можна звертатись тільки в тому блоку, де вони
оголошені. Змінні, що визначені зовні тіла функції, мають глобальну область дії і досяжні з будь-
якої функції у програмі, включаючи функцію  main().
 
Ім’я локальної змінної функції може співпадати з іменем глобальної змінної. Але така локальна
змінна не може змінити значення глобальної змінної. В тілі функції буде використовуватись саме
локальна змінна.
 
Глобальні змінні необхідні у тих випадках, коли програміст бажає зробити данні досяжними для
багатьох функцій. Помилки можуть виникнути, коли одна функція змінить значення змінної,
непомітно для іншої функції.
 
Не існує обмежень на тіло функції, але бажано, щоб воно не перебільшувало однієї сторінки
тексту для зручності читання. Функція має виконувати одну задачу, яку легко зрозуміти.
 
Аргументи, що передаються функції, є локальними змінними даної функції. Зміни, що внесені в
аргументи під час  виконання функції, не впливають на змінні, значення яких передаються у
функцію. В самій функції створюється локальна копія кожного аргументу.
 
Результатом роботи функції є вираз, константа та інше, що стоїть після ключового  слова Return.
 
Перевантаження функції
 
Мова С++ дозволяє створювати декілька  різних функцій з однаковим іменем.  Це називається
перевантаженням функцій. Такі функції повинні відрізнятись між собою списками параметрів:
типом або кількістю параметрів, або тим і тим одночасно. Наприклад:
 
int myf( int  );
int myf( int  , float );
int myf(float );
 
Функція myf( ) перевантажена з трьома різними списками параметрів, різними за типом та
кількістю. Типи значень, що повертаються з функції можуть бути однаковими або різними.
В результаті перевантаження функції відбувається явище поліморфізму функції. Це означає, що
в програмі існує декілька перевантажувальних функцій різного призначення.
Наприклад, функція подвоює значення, що їй передається. Такі значення можуть бути різного
типу int, long, float, double. Без перевантаження функцій необхідно було б створювати чотири
різні функції:
 
int Dd(int);
long Ddd(long);
float Dddd(float);
double Ddddd(double);
 
З допомогою перевантаження функцій достатньо використати наступне оголошення:
int D (int);
long D (long);
float D (float);
double D (double);
 
Достатньо передати потрібну змінну і потрібна версія функції буде викликана автоматично.
 
 
Вбудовані функції
 
Коли програмісти говорять про ефективність, то мається на увазі швидкість виконання програми,
а програма виконується швидше, якщо вдається уникнути звертань до функції. Кожен перехід до
області пам’яті, яка поміщає оператори функції, вповільнює виконання програми. Якщо функція
невелика, складається з одної чи двох інструкцій, то можна отримати деякий виграш в
ефективності, якщо замість переходів від програми до функції та назад, дати компілятору
команду вбудувати код функції за місцем виклику. Якщо функція  оголошена з ключовим
словом inline, компілятор не створює функцію в пам’яті комп’ютера, а копіює її рядки
безпосередньо в код програми за місцем виклику.
Якщо функція викликається багато разів, то за швидкість виконання програми прийдеться
заплатити розмірами програмного коду.
Приклад:
 
inline int myf(int n);

n=myf(n);

 
int myf(int n)
{
return 2*n;
}
 
Результат компіляції такого прототипу рівносильне заміні в програмі виклику функції стрічкою
n=2*n;.
 
 
 
Розподілення оперативної пам’яті при виклику функції.
 
Спеціальна область оперативної пам’яті, що виділяється для розміщення даних, необхідних при
кожному виклику функції, називається стеком. Стек представляє собою чергу типу „останнім
прийшов-першим вийшов”, тобто елемент, що добавлений в стек останнім, буде вибраним з
нього першим. В окремому місці, регістрі, фіксується вказівник вершини стеку. Все, що
знаходиться вище вершини, до стеку не відноситься.
При переході програми до виконання функції відбуваються наступні дії:

1. В стек поміщається адреса повернення по завершенню виконання функції.


2. В стеку резервується місце для результату роботи функції згідно повідомлення типу.
3. Текуча адреса вершини стеку зберігається в спеціальному вказівнику, який називається
записом активації. Все, що добавлено в стек з цього моменту і до завершення роботи
функції розглядається як локальні дані функції.
4. Всі аргументи функції поміщаються в стек.
5. Виконується перша команда функції.
6. Локальні змінні поміщаються в стек за чергою їх визначення.
7. По завершенню роботи функції її результат поміщається в стек.
8. Вказівник вершини стеку із запису активації пересилається у вказівник стеку, стек повністю
очищується, видаляються всі змінні та аргументи функції.
9. Результат роботи функції повертається зі стеку та присвоюється змінній, яка викликала
функцію.

 
Таким чином, при виклику функції адреса результату і параметри функції поміщаються в стек. На
протязі роботи функції в стек добавляються локальні змінні. При поверненні з функції всі вони
видаляються зі стеку.
Лекція 5
Лекція 5
Класи в С++
 
Поняття класу і об’єкту настільки пов’язані, що неможливо говорити про об’єкт не відносячи його
до класу. Але! Об’єкт означає конкретну сутність (визначену в часі і просторі), клас визначає
лише абстракцію.
Клас – це множина об’єктів, зв’язаних спільністю структури і поведінки. Інтерфейс на
частина класу може бути розділена на три складові частини:
-         загальнодоступна: та частина інтерфейсу класу, в якій даються визначення, „видимі” для
всіх об’єктів-користувачів даного класу;
-         захищена:  та частина інтерфейсу класу, в якій даються визначення, „видимі” лише для
об’єктів, що відносяться до підкласу даного класу;
-         відособлена: та частина інтерфейсу класу, в якій даються визначення, „приховані” для
об’єктів всіх інших класів.
Новий тип створюється в результаті оголошення класу. Клас – це набір змінних, одного або
різних типів, що є скомбінованими з набором функцій для роботи з ними. Клас може складатись з
будь-якої комбінації типів чи типів інших класів. Змінні в класі називаються змінними-членами.
Наприклад, класCar може мати змінні колеса, мотор, радіоприймач.
Функції в класі виконують дії над змінними-членами. Вони називаються функціями-членами або
методами класу. До методів класу Car можна віднести Start() (розганятися), Break() (гальмувати).
 
Відношення між класами.
 
ОО мови програмування реалізують в різних комбінаціях наступні механізми відношення класів:
-         наслідування;
-         використання;
-         представлення (наповнення);
-         мета класи.
Відношення наслідування: підклас може наслідувати структуру і поведінку свого
базового класу. Відношення наслідування включає такі основні елементи:
-         поліморфізм – такий елемент теорії типізації, який дозволяє використовувати одне ім’я для
позначення об’єктів різних класів, що мають спільний базовий клас В результаті об’єкт з таким
іменем може по-різному реагувати на виконання загального набору операцій.
-         множинний поліморфізм;
-         множинне наслідування.
 
Відношення використання: Наприклад, відношення між бібліотекою та книжками, бібліотека
об’єднує книги, але не є різновидом книги. Відношення використання мають два різних аспекти: в
інтерфейс ній частині одного класу може бути використаний інший клас або
інший клас використовується в реалізації. В першому випадку клас, що використовується,
повинен знаходитися в зоні „видимості” класів-користувачів. Наприклад, код функції, що виконує
контроль книг в бібліотеці, повинен бути „видимим” як для класу „Бібліотека”, так і для класу
„Книга”. В другому випадку клас, що використовується, знаходиться в області обмеженого
доступу класу, що використовує. Наприклад, в реалізації класу „Бібліотека” може бути
використаний клас „Список книг”. Для цього класу необов’язково виконувати вимогу „видимості”
для інтерфейсу класу „Бібліотека”. Достатньо виконати умову „видимості” для реалізації класу
„Бібліотека”.
Метаклас: клас класів, що дозволяє трактувати класи як об’єкти. Тобто це клас, екземпляри якого
є класами. С++ в явному вигляді не реалізує метакласи, але дозволяє створювати змінні класу і
методи класу.
 
Відношення наповнення: це фактично побудова збірного класу.
 
 
Взаємозв’язок класів та об’єктів
 
Класи і об’єкти тісно пов’язані поняття. Кожен об’єкт є екземпляром якогось класу, а клас може
породжувати довільне число об’єктів. Переважно класи статичні, тобто всі їх особливості і зміст
визначені в процесі компіляції програми. Об’єкти, навпаки, в процесі виконання програми
неперервно створюються і знищуються.
Для оцінки класів і об’єктів можна виділити 5 критеріїв:
1)    взаємозалежність (ступінь глибини зв’язків між окремими модулями);
2)    зв’язність (ступінь взаємодії між елементами окремого модуля, окремого класу або об’єкту.
Найбільш важлива функціональна зв’язність.)
3)    достатність (наявність в класі або модулі програми всього необхідного для реалізації логічної
і ефективної поведінки. Тобто компоненти повинні бути готові для застосування.)
4)    повнота (наявність в інтерфейс ній частині класу всіх необхідних характеристик
абстракції. Клас (або модуль) повинен гарантувати все необхідне для взаємодії з користувачем.)
5)    простота.
 
 
Оголошення класу
 
 Для оголошення класу використовується ключове слово class, за яким у фігурних дужках
записується список змінних-членів та методів класу через ;. В кінці ставлять ;.
Наприклад:
class Cat
{
int Age;
int Weight;
void Meow();
};
 
При оголошенні класу Cat пам’ять не резервується. Ця оголошення просто повідомляє
компілятору про існування класу, про те, які данні він включає ( змінні Age та Weight) та що він
вміє робити ( функція Meow()). Оголошення повідомляє компілятору про розміри класу виходячи
з типів змінних-членів. Для функцій-членів простір в пам’яті не резервується.
 
Визначення об’єкту
 
Об’єкт нового типу визначається так само як і будь-яка змінна.
int  Weight;    // змінна цілого типу
Cat Frisky;     //  визначення об’єкту класу Cat.
 
Frisky це об’єкт типу Cat. Об’єкт це конкретний екземпляр абстрактного класу.
 
Доступ до членів класу
 
Після визначення реального об’єкту класу Cat, наприклад Frisky, постає необхідність отримати
доступ до членів цього об’єкту. Для цього використовується оператор крапка (.), який дозволяє
звернутися до елементів об’єкту безпосередньо. Наприклад, щоб присвоїти число 50 змінній-
члену Weight об’єкту Frisky, можна написати
     Frisky. Weight=50;
Аналогічно для виклику методу Meow() достатньо написати
Frisky.Meow();
 
В мові С++ значення присвоюються змінним, а не типу. Тому наступний запис не допустимий:
Cat.Age=5;
Спочатку необхідно створити об’єкт класу Cat, а потім його змінній Age присвоїти значення 5:
Cat Frisky;
Frisky.Age=5;
 
Закриті та відкриті члени класу
 
При оголошенні класу використовуються два дуже важливих ключових слова: public (відкритий)
та private (закритий). Всі члени класу по-замовчуванню є закритими. До закритих членів класу
можуть звертатись лише методи даного класу. Відкриті члени доступні всім функціям програми.
Ця відмінність дуже важлива. Для її розуміння розглянемо наступний приклад:
class Cat
{
int Age;
int Weight;
void Meow();
};
В цьому класі всі змінні та функція є закритими. Якщо в функції main() написати
Cat Boots;
Boots.Age=5;
то компілятор виловить помилку, не можна звертатись до закритих даних. Такі помилки типові
для початківців. Об’єкт Boots в своїх власних методах може звертатись до всіх своїх елементів,
закритих та відкритих. Що дозволити доступ із зовні  до змінних-членів об’єкту, необхідно зробити
їх відкритими:
class Cat
{
public:
int Age;
int Weight;
void Meow();
};
 Тепер всі елементи класу відкриті і стрічка
Boots.Age=5;
не викличить помилку.
 
Закриті данні-члени
Змінні-члени класу необхідно залишати закритими. Завдяки цьому
забезпечиться інкапсуляція даних всередині класу. Щоб передавати та повертати значення
закритих змінних необхідно створити відкриті функції, відомі як методи доступу.
Відкритий метод доступу – це функція-член класу, яка працює з закритими змінними-
членами класу. Застосування методів-доступу дозволяє приховати від користувача подробиці
зберігання даних в об’єктах. В той же час є методи використання цих даних. В результаті можна
модернізувати способи зберігання та обробки даних всередині класу, не переписуючи при цьому
методи доступу та виклики їх в зовнішньому програмному коді.
Наприклад, якщо зовнішня функція отримує змінну Age класу Cat через функцію-член GetAge(),
то клас Cat можна змінювати і це не вплине на можливості функції  GetAge() в основній програмі.
Такий підхід продовжує життєвий цикл програми, оскільки внесені зміни не чіпають програму в
цілому.
Приклад:
 
clas Cat
{
public:
     //відкритий метод доступу
      int GetAge();
      void SetAge(int Age);
     //відкрита функція-член
       void Meow();
     //закриті змінні-члени
private:
   int Age;
};
 
Метод доступу SetAge()встановлює значення змінної, а метод GetAge() - повертає значення.
Щоб встановити вік Frisky, необхідно передати відповідне значення методу SetAge() :
Cat Frisky;
Frisky.SetAge(5);
 
Реалізація методів класу
Для кожної функції доступу, як і для інших методів класу, має існувати реалізація. Реалізація – це
опис дій функції. Реалізація функції-члена починається з імені класу, за яким ставиться дві
двокрапки :: , далі ім’я функції та її параметри.
Наприклад, для відкритої функції доступу GetAge( ), яка повертає значення змінної Age:
int Cat ::  GetAge( )
{
return Age;
}
Наприклад, для відкритої функції доступу SetAge( ), що встановлює значення елементу itsAge:
void Cat :: SetAge(int age)
{
itsAge =age;
}
 
Конструктори та деструктори
 
Щоб ініціалізувати змінні-члени класу в класі використовується спеціальна функція-член
– конструктор. Це є метод класу, ім’я якого співпадає з іменем класу. Конструктори служать для
створення та ініціалізації об’єктів класу. Конструктор може приймати значення, але нічого не
повертає.  Деструктори видаляють з пам’яті відпрацьовані об’єкти та звільняють виділену для них
пам’ять. Деструктор має ім’я класу з символом тільда ~ на початку. Деструктори не приймають та
не повертають значень.
Наприклад, для класу Cat
Cat ( );  //конструктор, який нічого не приймає
~Cat( ); // деструктор
 
Якщо не оголошувати конструктор та деструктор, то компілятор зробить це сам.
Такі конструктори та деструктори називаються стандартними, вони не приймають аргументів та
нічого не виконують.
Всі об’єкти в програмі мають бути спочатку створені, а потім знищені. Це і є функцією
стандартних конструктора та деструктора.
Наприклад, конструктор для нового об’єкту Frisky класу Cat, що приймає два параметра:
Cat Frisky (5,7);
 
Стандартний конструктор для цього ж об’єкту:
Cat Frisky ;
 
Постійні функції-члени
 
Якщо оголосити метод класу як постійний const , то він не зможе змінити значення ні одного
члену класу.
void Function( ) const;
Функція доступу GetAge( ) нічого не змінює, тому її варто оголосити постійною:
int GetAge( ) const;
 
Використовувати const при об’яві методів, які не змінюють об’єкт, вважається гарним тоном в
програмуванні. Це дозволяє компілятору знаходити помилки до того, як вони стануть причиною
проблем.
 
Де оголошувати та розміщувати реалізацію методів?
 
Кожна функція, що об’явлена у класі має бути визначена. Визначення функції називається також
реалізацією. Реалізація функцій або методів класу має заголовок та тіло. Реалізація функції має
знаходитись у файлі, який є досяжним для компілятора. Це є файли з розширенням .c або .cpp
Оголошення класів можна розмістити в один файл з програмою, але це не є гарним тоном.
Прийнято розміщати оголошення в файл заголовка, ім’я якого переважно співпадає з іменем
файлу програми та має розширення .hpp (.h,.hp). Наприклад, можна розмістити
оголошення класу Cat у файл Cat.hpp, а реалізацію методів класу – у файл Cat.cpp. Файл
заголовка необхідно включити у файл коду. Для цього у файлі Cat.cpp перед початком
програмного коду використовується директива
#include “Cat.hpp”
Це є повідомлення для компілятора внести вмістиме файлу Cat.hpp в певне місце програми.
Клієнта класу переважно не дуже хвилюють подробиці реалізації класу. Не виключено, що файл
заголовка (.hpp) вдасться застосувати не в одному, а в декількох файлах програми.
Таким чином, файл заголовка – це файл з розширенням .hpp, де розміщене оголошення класу з
переліком його змінних-членів та функцій-членів або методів.
 
Вбудована реалізація
 
Методи класу можна зробити вбудованими. Для цього перед типом значення що повертається
необхідно розмістити ключове слово inline . Наприклад,
іnline int Cat : : GetWeight ( )
{
  return itsWeight;
}
 
Реалізацію методу можна помістити при об’яві класу, що зробить таку функцію вбудованою
автоматично. Наприклад,
class Cat
{
          public:
      int GetWeight ( ) const { return itsWeight; }
      void SetWeight (int aWeight);
      private:
      int itsWeight;
};
 
Тіло вбудованої функції починається одразу після оголошення і береться у фігурні дужки.
 
Структури
 
Дуже близьким родичем ключового слова class є слово struct, яке використовується для
оголошення структури. В мові С++ структура це є клас, всі члени якого по замовчуванню є
відкритими (public). Структуру можна оголосити так само як клас з переліком змінних-членів та
методів. Так чому два різних ключових слова виконують однакові дії? Так склалось історично.
Коли розроблялась мова С++, за основу була взята мова С, яка включала структури. Але ці
структури не мали методів. Якщо взяти будь-яку програму на мові С++ і замінити в ній слово class
на слово struct, то ніякої різниці в результатах ви не побачите.

Лекція 6
Лекція 6
Вказівники
 
Вказівник – це змінна, яка зберігає адресу в пам’яті комп’ютера, це адреса оперативної пам’яті.
Оперативна пам’ять розділена на послідовно пронумеровані комірки. Кожна змінна розміщається
в одній або в декількох послідовно розташованих комірках пам’яті. Адреса першої такої комірки
називається адресою змінної. Ця адреса і зберігається у вказівнику на таку змінну. Адресу змінної
можна отримати з допомогою оператора звернення до адрес (&).  Наприклад, для
змінної m адреса буде &m . Цей  результат присвоюється іншій змінній, яка і
називається вказівником на змінну m . Змінну, яка буде виконувати роль вказівника необхідно
оголосити:
int * pAge = 0;
pAge є вказівником на змінну цілого типу. Переважно розмір вказівника 4 байти. Тип перед *
вказує компілятору на змінну якого типу оголошується вказівник. Переважно за домовленістю
ім’я вказівника починається літерою p
 
Вказівники, значення яких дорівнює 0 називаються пустими. Після оголошення вказівнику
обов’язково треба присвоїти якесь значення. Якщо заздалегідь не відомо, яка адреса буде
зберігатись у вказівнику, то йому присвоюють значення 0.
Непроініціалізований вказівник називають диким, такі вказівними дуже небезпечні.
Наприклад,
int Age = 50;
int * pAge = 0;
pAge = &Age;  // присвоїти адресу змінної  Age вказівнику pAge.
 
З допомогою вказівника можна отримати значення змінної, на яка він вказує. Доступ до значення
змінної через її вказівник називається непрямим. Оператор непрямого доступу (*), також
називається оператор взяття значення або посиланням. При вилучення значення з вказівника
буде повернено таке значення, яке зберігається за адресою, що занесена у вказівник.
Наприклад, щоб присвоїти значення змінної Age іншій змінній Old можна записати:
Old = Age;
А через вказівники:
Old = * pAge;
 
Дуже важливо розрізняти вказівник, адресу, що зберігається у вказівнику та значення, що
розміщено за адресою, яка зберігається у вказівнику.
int Age = 50; // змінній цілого типу присвоєно значення 50
int * pAge = &Age;   // вказівнику цілого типу присвоєна адреса змінної Age
Значення, яке записано за адресою, що зберігається у вказівнику *Age дорівнює 50.
 
Найчастіше вказівники застосовуються в наступних випадках:
-         керування даними у вільній області пам’яті;
-         доступ до змінних-членів та функцій класу;
-         передача даних між функціями за посиланням.
 
Арифметичні операції з вказівниками
 
Один вказівник можна віднімати від іншого. Якщо два вказівника посилаються на різні елементи
масиву, різниця цих вказівників дасть кількість елементів масиву, що знаходиться між
зазначеними елементами. Найбільш ефективно використовується ця методика при обробці
символьних масивів.
 
Вказівники та константи
 
Розглянемо три випадки:
1)    const int * pOne;
2)    int * const pTwo;
3)    const int * const pThree;
Всі наведені оголошення дійсні.
pOne є вказівник на константу типу int. Значення, на яке він вказує не може бути зміненим.
pTwo є константою-вказівником на тип int. Саме ціле число може бути змінене, але адреса
у вказівнику – ні.
pThee оголошено як константа вказівник на константу типу int. Такий вказівник завжди вказує на
ту саму область пам’яті, и значення, яке знаходиться за цією адресою  не може бути змінене.
 
Якщо оголосити метод класу як const, то він не зможе змінити ні одного з членів класу.
Якщо вказівник оголошений на об’єкт, що є const, то методи, які можна викликати з його
допомогою, також мають бути постійними.
Ключове слово const дозволяє захистити об’єкт, який досяжний за посиланням.
 
Стек та динамічна пам’ять
 
Перерахуємо основні області пам’яті:
-         область глобальних змінних;
-         вільна або динамічно-розподілена пам’ять;
-         регістри;
-         стек.
Локальні змінні та параметри функцій розміщаються у стеку. Об’єктний код програм – в
сегментах, а глобальні змінні – в області глобальних змінних. Регістри використовуються для
внутрішніх потреб процесору. Решта частина пам’яті складає так звану вільну пам’ять або
динамічно-розподілену пам’ять. Особливістю локальних змінних є те, що після виходу з функції, в
якій вони були оголошені, пам’ять, що виділена для їх зберігання звільнюється, а значення
змінних знищуються.
Глобальні змінні дозволяють частково вирішити цю проблему за рахунок необмеженого доступу
до них з будь-якого місця програми , але це ускладнює сприйняття тексту програми.
Використання динамічної пам’яті повністю вирішує обидві проблеми.
Динамічно-розподілену пам’ять можна представити як величезний масив послідовно
пронумерованих комірок для зберігання даних. На відміну від стеку коміркам вільної пам’яті не
можна надати ім’я, але можна у вказівнику запам’ятати адресу першої такої комірки.
Динамічна пам’ять не очищується до завершення програми. Тому відповідальність за очищення
всієї пам’яті повністю накладається на програміста. Область динамічної пам’яті не може бути
використана для інших потреб, доки не буде звільнена явно.
Перевагою динамічної пам’яті є те, що доступ до даних можна отримати лише з тих функцій, які
володіють доступом до вказівника на область динамічної пам’яті. Це дозволяє уникнути
випадкової зміни даних.
 
Оператор NEW
 
Для виділення ділянки пам’яті у динамічно-розподіленій області використовується ключове
слово new. Далі треба вказати тип об’єкту, який буде розміщений в пам’яті. Результатом роботи
оператора new є адреса виділеного фрагмента пам’яті. Ця адреса має бути
присвоєна вказівнику. Наприклад,
int * pR;
pR = new int;
або інакше
int * pR = new int;
 
З допомогою цього вказівника можна передавати значення у виділену область пам’яті.:
*pR = 70; // розмістити число 70 в той області динамічної пам’яті, на яку вказує pR
 
Якщо оператор new не зможе виділити місце у динамічно-розподіленій пам’яті (через її
відсутність), то він передасть виключення, про яке мова буде пізніше.
 
Оператор delete
 
По завершенню роботи з виділеною областю пам’яті її необхідно звільнити. Це робиться з
допомогою оператора delete :
delete pR;
Необхідно пам’ятати, що сам вказівник є локальною змінною. Тому, коли функція, яка
оголосила вказівник, завершує роботу, вказівник виходить з області дії, а записана у ньому
адреса губиться. Пам’ять, що виділена оператором new не звільняється автоматично. Якщо ж її
адреса буде загублена, то її не можна ні використати, ні видалити. Така ділянка пам’яті стає
абсолютно недосяжна і така ситуація називається утіканням пам’яті.
Після роботи оператора delete сам вказівник залишається і в нього можна  записати іншу адресу.
Якщо застосувати оператор delete два рази до одного вказівника, між якими не буде оператора
new, то це призведе до зависання програми. Тому рекомендується при звільнені області
динамічної пам’яті присвоїти пов’язаному з нею вказівнику значення 0:
delete pR;
pR = 0;
 
Ще одна можлива неприємність пере присвоїти вказівник, попередньо не звільнивши ділянку
пам’яті, на яку він вказував. Це призведе до утікання пам’яті. Наприклад:
int * pR = new int;
*pR = 72;
pR = new int;
*pR = 84;
 
Тепер вихідна ділянка пам’яті, яка зберігає значення 72, є недосяжною, оскільки вказівнику на цю
ділянку було приписано нове значення. В результаті не можливо ні звільнити, ні використати
зарезервовану пам’ять до завершення програми. Правильно треба було записати так:
int * pR = new int;
*pR = 72;
delete pR;
pR = new int;
*pR = 84;
 
Кожний раз, коли в програмі використовується оператор new за ним має бути десь в програмі
записаний оператор delete.
 
Створення об’єктів в області динамічної пам’яті.
 
В динамічній пам’яті можна розміщати об’єкти будь-яких класів. Наприклад, якщо створити об’єкт
Cat , то для маніпулювання з ним можна створити вказівник, який буде зберігати адресу об’єкту:
Cat *pCat = new Cat; // вказівник pCat типу класу Cat? В правій частині тип об’єкту є клас Cat.
В даному випадку в операторі new використано конструктор класу по замовчуванню, без
параметрів.
 
Видалення об’єктів
 
При використанні оператора delete для вказівника на об’єкт
викликається деструктор відповідного класу. Це відбувається ще до очищення ділянки пам’яті. Це
дозволяє об’єкту класу коректно закінчити своє існування.
Наприклад:
Cat *pCat = new Cat;
delete pCat;
 
Доступ до змінних-членів об’єкту в динамічній пам’яті
 
Для доступу до змінних-членів та функцій об’єкту Cat, який створений звичайним способом,
використовується оператор крапка (.).Для доступу до змінних-членів та функцій об’єкту Cat, який
створений в динамічній пам’яті, необхідно використовувати посилання на вказівник та оператор
крапку. Наприклад,
Cat.GetAge ( ); //доступ до функції-члену об’єкту Cat
 // доступ до функції-члену об’єкту Cat, що розміщений у динамічній пам’яті.
Спочатку відбувається звернення до значення за адресою у вказівнику, а потім до функції
об’єкту. В мові С++  таке звернення можна записати простіше, за допомогою оператора
непрямого доступу (->) наприклад,
Cat * pFrisky = new Cat; // в динамічній пам’яті створюється об’єкт класу Cat
pFrisky -> SetAge(5); // метод SetAge встановлює значення 5
delete pFrisky; // очищення ділянки пам’яті.
 
Змінними-членами класу можуть бути вказівники на об’єкт, що розміщений у динамічній пам’яті.
Пам’ять може буди виділена в конструкторі або в одному з методів класу, а звільнена
в деструкторі класу. Наприклад,
class  Cat
{
 public:
   Cat ( );
   ~Cat ( );
…..
private:
  int * itsAge;
  int * itsWehgt;
};
 
Cat : : Cat( )
{
  itsAge = new int (2); // виділення динамічної пам’яті та ініціалізація        
                                    // змінної itsAge=2
  itsWehgt = new int (5); // виділення динамічної пам’яті та ініціалізація        
                                    // змінної itsWehgt =5
 
}
 
Cat : : ~Cat ( )
{
  delete itsAge;
  delete itsWehgt;
}
…..
int main ( )
{
Cat *Frisky = new Cat;
…..
delete Frisky;
return 0;
}
 
При видаленні об’єкту Frisky викликається деструктор класу Cat. Деструктор видаляє кожний
з вказівників, що належать об’єкту.
 
Вказівник this
 
Кожний метод класу має прихований параметр: вказівник this. Цей вказівник поміщає адресу
поточного об’єкту. При кожному зверненні до функцій GetAge ( )  або SetAge ( ) вказівник об’єкту
this включається як прихований параметр. Наприклад,
void SetAge (int Age)
   {this -> itsAge = Age;}
int GetAge ( ) const
  { return itsAge;}
 
Функції доступу SetAge ( ) та  GetAge ( ) використовують вказівник this для доступу до змінної-
члену класу.
 
 

Лекція 7
Лекція 7
Посилання
 
Посилання – це псевдонім; при створенні воно ініціалізується іменем іншого об’єкту, адресату. З
цього моменту посилання є альтернативне ім’я адресату, і все, що діється з посиланням,
відбувається і з об’єктом.
При оголошенні посилання вказується тип об’єкту, оператор посилання (&) та ім’я посилання. 
Переважно ім’я посилання починається з літери r.
int &rRef = someInt; // rRef є посиланням на цілочисельне значення, яке знаходиться у змінній
someInt.
На відміну від вказівника посилання необхідно ініціалізувати при оголошенні.
Посилання не можна переназначати. При цьому відбудеться присвоєння нового значення
адресату.
Посилатися можна на будь-який об’єкт, але не на клас:
 
Cat Frisky;
Cat &rCatRef = Frisky;
 
За посиланнями можна звернутись до змінних-членів та методів об’єкту через оператор крапки:
 
rCatRef.GetAge( )
 
Посилання не може мати значення 0, така програма вважається не коректною.
 
 
 
 
Передача функції аргументів за посиланням.
 
Функції мають два обмеження:
-         аргументи передаються як значення і гублять зв’язок з вихідними даними;
-         повертати функція може лише одне значення.
За допомогою посилання можна уникнути цих обмежень. Передати аргумент за посиланням
можна використовуючи або вказівник, або посилання. В цих обох випадках функція використовує
не копію, а справжній об’єкт. В стек поміщається не сам об’єкт, а його адреса. При передачі
функції об’єкту за посиланням вона може змінювати сам об’єкт.
Приклад програми передачі параметрів за значенням:
 
void swap (int x, int y);
int main ( )
{
  int x=5, y=10;
  swap (x,y);
  return 0;
}
 
void swap (int x, int y)
{
  int temp;
  temp=x;
  x=y;
  y=temp;
}
 
Функція swap ( ) має міняти місцями x та y. Але при поверненні в головну програму x та y
залишаються ті самі. Причина в тому, що передача змінних відбувається за значеннями і у
функції swap ( ) це є локальні копії самих змінних. У самій функції вони міняються місцями, але 
при поверненні до головної програми локальні змінні губляться.
Для вирішення цієї проблеми треба передати значення змінних за посиланням, тобто
через вказівники або через посилання.
Приклад передачі аргументів за вказівниками:
 
void swap (int *x, int *y);
 
int main ( )
{
  int x=5, y=10;
  swap (&x,& y);
  return 0;
}
 
void swap (int *px, int *py)
{
  int temp;
  temp = *px;
  *px = *py;
  *py = temp;
}
 
У прототипі функції swap ( ) параметрами оголошені вказівники на значення типу int. При виклику
функції передаються адреси змінних. У функції вони міняються місцями, відповідно міняються
значення, які знаходяться за цими адресами.
Приклад передачі аргументів за посиланнями:
 
void swap (int &x, int &y);
 
int main ( )
{
  int x=5, y=10;
  swap ( x, y);
  return 0;
}
 
void swap (int &rx, int &ry)
{
  int temp;
  temp = rx;
  rx = ry;
  ry = temp;
}
 
Результат буде однаковий. Змінні x та y помінялись місцями. У виклику функції передаються самі
значення змінних, які у функції ідентифікуються як посилання. Зміна місцями відбувається і у
функції і у головній програмі, так як посилання є псевдонімом вихідних значень.
 
В мові С++ клієнт класів та функцій всю необхідну інформацію переглядає у файлі заголовку. Цей
файл виконує роль інтерфейсу з класами та функціями, сама реалізація яких прихована від
клієнтів. Це дозволяє програмісту зосередитись на власних проблемах та
використовувати клас або функцію, не розбираючись в подробицях їх роботи. Такі складові
частини можна зібрати в одну програму, знаючи лише, що робить функція або класчерез їх
прототипи.
 
Друга ситуація, коли необхідно з функції повернути одночасно два значення. Рішенням цієї
проблеми є передача функції за посиланням двох об’єктів. В ході виконання функція присвоює
цім об’єктам потрібні значення. При передачі об’єктів за посиланням стає можливим змінювати ці
об’єкти у функції. Наприклад:
 
void Factor (int n, int *pSquared, int *pCubed);
 
int main( )
{
  int number, squared, cubed;
 
 Factor (number, &squared, &cubed);
 
 return 0;
 
void Factor (int n, int *pSquared, int *pCubed)
{
 *pSquared = n*n;
 *Cubed = n*n*n;
}
 
Значення n та адреси змінних squared, cubed передаються функції Factor( ) у вигляді параметрів.
З допомогою вказівників змінним з головної програми присвоюються значення, які  і
повертаються у викликаючу програму. Аналогічно відбувається повернення значень
через посилання.
Передача та повернення об’єктів у/з функції як значення вимагає необхідність виклику
спеціальних конструкторів копій та деструкторів для видалення тимчасових об’єктів. Це зайвий
час та об’єм пам’яті. Тому передача об’єктів через посилання (за вказівником або посиланням)
дозволяє оптимізувати програму за ресурсами. Але при цьому може виникнути неприємність:
об’єкт стає відкритим для змін, анулюється його захист. Вирішити цю проблему можна через
передачу функції вказівника на постійний об’єкт певного класу. Тоді до об’єкту можна застосувати
тільки постійні методи, які не можуть його змінити.
 
const Cat * const Function (const Cat * const theCat);
 
І параметр і значення, що повертається оголошені як постійні вказівники на постійні об’єкти, до
яких не можуть бути застосовані непостійні методи, наприклад, SetAge ( ).
Аналогічно можна передавати посилання на постійний об’єкт:
 
const Cat & const Function (const Cat & const theCat);
 
Якщо до посилання застосоване ключове слово const, то воно робить постійним не посилання, а
пов’язаний з ним об’єкт.
 
Переваги посилання
 
Досвідчені програмісти віддають перевагу посиланням, а не вказівникам. Посилання простіше
використовувати і вони дозволяють приховати інформацію. Посилання не можна переназначати,
вони не можуть приймати нульове значення, до них не можна застосувати оператор new. У таких
випадках застосовують вказівники.
Рекомендується передавати функціям параметри та повертати значення за посиланням скрізь,
де це можливо. Використовуйте оператор const для захисту посилань та вказівників. Не
використовуйте вказівними, якщо замість них можна використати посилання.
Допустиме оголошення у списку параметрів функції одночасно і вказівники, і посилання, і об’єкти,
що передаються за значенням. Наприклад:
Cat *Function (P &rP, h *pH, int a); // посилання на об’єкт типу P, вказівник на об’єкт типу H, та ціла
змінна а.
 
Відступи та пробіли в мові С++ повністю ігноруються, там де стоїть один пробіл можна поставити
і більше. Головне вибрати стиль і строго його притримуватись на протязі всієї програми.
Рекомендується розміщати символи &  та * біля імені змінної та з пробілом зліва:   *pH,  &rT.
Ніколи не оголошувати посилання, вказівники та змінні в одній стрічці.
При передачі посилання у функцію або з неї поставте запитання : „Чим є об’єкт, посилання на яке
використовується, чи буде він існувати на момент його застосування?”. Не
повертайте посилання на об’єкт, який знаходиться зовні області дії! Якщо об’єкт є локальним у
функції, то при повернені з функції він буде зруйнований, тому посилання залишиться на
неіснуючий об’єкт, а це погано.
 
Отже закінчуючи розмову про посилання важливо запам’ятати: посилання завжди повинні бути
ініціалізовані існуючими об’єктами та їх не можна пере назначати до кінця
програми.  Посилання це псевдонім об’єкту і будь-яка дія над посиланням виконується і над
об’єктом. Передача об’єктів у функцію за допомогою посилання більш ефективна ніж за
значенням, бо дозволяє змінювати значення змінних головної програми. Рекомендується
використовувати вказівники на постійні об’єкти та постійні вказівники на об’єкти з метою
забезпечення безпеки передачі значень між функціями або для підвищення ефективності роботи
програми.

Лекція 8
Лекція 8
 
Перевизначення операторів. Дружні функції.
 
С++ підтримує спеціальні засоби, які дозволяють перевизначити вже існуючі операції. Наприклад,
для операції + можна ввести своє власне визначення, яке реалізує операцію додавання для
об’єктів певного класу. Фактично пере визначення для операцій існує і в мові С. Так, операція +
може використовувати як об’єкти типу int, так і об’єкти типу float. С++ розширює цю ідею.
Для визначення операції використовується функція, що вводиться користувачем. Тип функції
визначається іменем класу, далі записується  ключове слово operator, за яким слідує символ
операції, в круглих дужках дається перелік параметрів, серед яких хоча б один типу клас.
 Наприклад, введемо операцію + для класу complex косплексних чисел.
class complex
{
 double real, imag;// дійсна та уявна частини
 public:
complex ( double r, double i ) { real = r; imag = i; }// конструктор з ініціалізацією
complex operator+ (complex c1, complex c2)
{return complex (c1.real + c2.real, c1.imag + c2.imag); }// перевизначена функція додавання
}
 
int main()
{ complex c1(0, 1), c2(1,0), c3;// об‘єкти типу клас complex
c3 = c1 + c2;// скорочений запис
c3=operator+(c1,c2); // явний виклик функці.ї Результат однаковий.
return 0;
}
 
Функція operator+ повертає результат типу complex та має параметри С1 та С2 типу complex.
Функції-операції мають бути нестатичними функціями-членами класу або мати мінімум один
аргумент типу класу. За виключенням операції присвоєння всі перевизначені оператори
наслідуються.
Приклад, перевизначення операції множення простих дробів.
class drob
{
public:
int Ch, Zn;
drob (int c, int z ) { Ch=c; Zn=z; }// конструктор
drob operator * (drob);
};
drob drob :: operator * (drob S1)
{ drob R;// результат
R.Ch = Ch * S1.Ch;// чисельник результату
R.Zn = Zn * S1.Zn;// знаменник результату
Return R;
}
 
int main()
{ drob D(4,5);//перший дроб 4/5
drob E(2,3);// другий дроб 2/3
drob S;
S=D*E; // аналогічно  S=D.operator*(E)
return 0;
}
 
Перевизначення унарних операцій.
Унарними називаються операції, що мають один аргумент. Якщо @ - унарна операція, то @x
інтерпретуються як 1) x.operator@( ) – нестатична функція-член класу, що не має параметрів; 2)
operator@(x) -  функція, що не є членом будь-якого класу і має один аргумент. Цей аргумент є
змінною або посиланням на змінну даного класу. Звідси слідує, що при перевизначені губиться
різниця між префіксними та постфіксними унарними операціями.
 
Перевизначення бінарних операцій.
Якщо @ - бінарна операція, то x@y інтерпретується як 1) x.operator@(y) – нестатична функція,
що має один аргумент; 2) operator@(x,y) – як функція не член класу, що має два аргументи.
 
Перевизначення операції присвоєння
Така операція використовується для присвоєння значення одного об‘єкту даного класу іншому.
class Cat
{
 public:
int GetAge();
int GetW ();
Cat & operator= (Cat &);
private:
int *Age;
int *W;
};
Cat & Cat :: operator= (Cat &h)
{
*Age = h.GetAge();// присвоєння одних змінних іншим
*W = h.GetW();
return *this; //повернення вказівника об‘єкту
}
 
int main()
{ Cat Frisky;
Cat Whiskers;

Whiskers=Frisky; // присвоєння одного об‘єкту іншому
return 0;
}
Зверніть увагу на використання посилкових типів в операції =. Перевизначена
операція присвоєння не наслідується. Якщо для будь-якого класу Х нема введеної користувачем
операції = , то по замовчуванню операція = визначається як поелементне присвоєння для всіх
членів класу.
 
Перевизначення операції ->
Доступ до члену класу розглядається як унарна операція. Вираз x-m, де х є об‘єкт класу Х
інтерпретується як (x.operator->( )) – m, так що функція operator->( ) має повертати вказівник на
об‘єкт класу. Має бути визначена як нестатична функція-член класу.
 
Загальні правила перевизначення.

1. С++ не розрізняє перевизначені префіксні та постфіксні версії ++ та --.


2. Операція, яка перевизначається , має існувати в мові.  Наприклад, неможна визначати
оператор #.
3. Неможна перевизначити наступні операції:  .  ::  ? :
4. Пріоритет операцій при перевизначенні зберігається.
5. Не можно змінити число операндів операції, тобто унарна для 1-го операнду, бінарна для
2-х.

 
Дружні функції
 
Дружньою функцією класу називається функція, яка сама не є членом класу, але має повні права
на доступ до закритих та захищених елементів класу. Оскільки така функція не є членом класу,
то вона не може бути вибрана з допомогою операторів (.) та (->), Дружня функція класу
викликається звичайним способом. Для опису дружньої функції використовується ключове
слово friend. Наприклад,
class X
{
  int i;
  friend void F( X *, int);
  public:
void M (int);
};
void F(X *px, int a)
{ px ->i=a;} //доступ до закритого елемента через вказівник об‘єкту
void X ::M(int a)
{i=a;} //прямий доступ до закритого елемента
 
int main( )
{  X mm;
  F(&mm, 6);//виклик фружньої функції
  mm.M(6);//виклик методу класу
}
 
З допомогою опису  friend ім‘я класу можна оголосити всі функції класу дружніми для
іншого класу:
 
class Y
{
  void f1(X &);
  void f2(X *);
};
class X
{
 friend Y;
 int i;
 int M( int);
};
Функції можуть бути дружніми більш ніж одному класу.
Також можна оголосити деякі окремі функції-члени класу дружніми для іншого класу:
class X
{...
  void f1(int);
...};
class Y
{
   int i;
   friend void X::f1(int);
};
 
Відношення дружності не є транзитивним: Х дружній Y, Y дружній Z, але не обов‘язково Х
дружній  Z.

Лекція 9
Лекція 9
 
ПОТОКИ ВВОДУ-ВИВОДУ
 
С++  підтримує стандартну бібліотеку iostream , яка повністю відповідає за ввід та вивід даних.
Перенос обробки операцій вводу-виводу в спеціальні бібліотеки дозволив отримати ефект
незалежності від платформи. Це дозволяє створити програму на С++ для комп‘ютерів РС, а потім
відкомпілювати її для робочої станції SUN. Класи iostream розглядають інформацію як побітовий
потік даних. Якщо дані виводяться у файл або на екран, джерело потоку включено в програму.
Якщо потік направлений в протилежну сторону, то дані поступають з клавіатури або файлу у
змінні. Головна задача потоків – інкапсуляція процедури обміну інформацією з диском або
дисплеєм комп‘ютера.
Запис на диск та вивід на екран потребують багато часу, що призводить до блокування
виконання програми. Для вирішення цієї проблеми потоки підтримують буферизацію.  Спочатку
дані записуються у буфер потоку, а після його наповнення уся інформація разом записується на
диск.
Реалізація потоків та буферів відбувається засобами об‘єктно-орієнтованого підходу С++:
-         клас streambuf  керує буфером, наповнює його, очищує та скидає;
-         клас ios є базовим для класів потоків вводу-виводу. В якості змінної класу ios є
об‘єкт streambuf  ;
-         кккласи istream та ostream є похідними від класу ios та відповідають відповідно за
потоковий ввід та вивід даних;
-         клас iosteam є похідним від класів istream та ostream і забезпечує функції вводу з
клавіатури та виводу на екран;
-         клас fstream використовується для операцій вводу-виводу з файлів.
При запуску програми, що включає клас iostream створюються 4 об‘єкти:
1)    об‘єкт CIN – обробляє ввід з клавіатури;
2)    об‘єкт COUT – обробляє вивід на екран;
3)    об‘єкт CERR – обробляє вивід повідомлень про помилки на екран;
4)     об‘єкт CLOG – обробляє пере адресацію повідомлень про помилки у файл регістрації.
 
Ввід даних через об‘єкт CIN
 
Об‘єкт CIN включає перевантажений оператор вводу >>, який записує дані з буфера в локальну
змінну.
 
int a;
cin >> a;
 
Оператор вводу перевантажений так, що підходить для вводу даних всіх основних типів int, short,
long, char, float, double. Коли компілятор зустрічає вираз cin >> a; то запускається той варіант
вводу, який  відповідає типу змінної а. Для попереднього прикладу це буде варіант
перевантаженого оператора вводу для цілого типу
 
istream& operator >> (int &)
 
Приклад:
 
#include <iostream>
void main()
{ int myInt;
  float myFloat;
cin>>myInt;
cin>> myFloat;
}
 
Об‘єкт CIN може приймати аргументом вказівник на стрічку символів. Але при цьому пробіл
сприймається як роздільник стрічок і при зустрічі пробілу  cin завершує ввід, додає кінцевий
нульовий символ.
 
char YourName[50];
cin>> YourName;
 
Функції об‘єкту CIN
 
1. Функція get() служить для зчитування одного символу зі стандартного пристрою вводу. Цей
символ потоку вводу присвоюється символьній змінній, що передається функції get() як
параметр.
 
char a;
cin.get(a);
 
Для вводу символьного масиву необхідно вказати в параметрах функції  get() ім‘я масиву та
максимальну кількість символів з врахуванням кінцевого нульового символу.
 
char Mas[256];
cin.get(Mas, 256);
 
2. Функція ignore() ігнорує частину символів до кінця стрічки або до кінця файлу.  Вона приймає
два параметри – кількість символів, що пропускається та символ закінчення.
 
ignore(80, ‘\n’);
 
Програма пропустить ввід 80 символів, якщо раніше не зустріне символ нової стрічки. Він також
видаляється з буферу і функція завершує роботу.
 
3. Функція peek() проглядає але не вводить один символ з буфера.
4. Функція  putback() вставляє один символ в потік вводу.
Приклад, замінити всі знаки оклику при вводі знаком долара і видалити всі знаки решітка.
#include <iostream>
void main()
{ char ch;
   while (cin.get(ch))
{ if (ch ==’!’)
       cin.putback(‘$’); // не вводити ! а вводити $
   else   cout << ch;
while ( cin.peek() ==’#’)
    cin.ignore(1, ‘#’); // пропустити знак #
}}
 
Вивід даних через об‘єкт COUT
 
Об‘єкт COUT дозволяє форматувати дані, виводити на екран стрічки, числа, вирівнювати стовбці,
виводити числові значення в десятковому та шіснадцятковому форматі. Перед виводом даних
рекомендується провести очистку буфера. Це робить функція flush() об‘єкту COUT.
cout << flush();
 
Функції об‘єкту COUT
 
1. Функція put() виводить окремі символи.
 
cout.put(‘Y’);
 
2. Функція write() працює так само, як оператор вводу<< , але вона приймає параметр, що вказує
максимальну кількість символів при виводі.
 
char Mas[ ] = “ab c def”;
int len=strlen(Mas); // 8
int s=len-4; // 4
cout.write(Mas, len)<< “\n”; // ab c def
cout.write(Mas,s)<< ”\n”; // ab c
 
3. За замовчуванням ширина поля виводу автоматично встановлюється так, щоб вмістити всі
символи стрічки з буфера виводу. Але з допомогою функції width() можна встановити точне
значення ширини поля виводу. Ця функція є членом об‘єкту COUT.
 
cout.width(4);
cout << 12;//_ _ 12
 
4. Щоб заповнити пусті позиції певним символом , на приклад,   «*» використовують функцію fill(),
в параметрі якої вказують символ заповнення.
 
cout.fill(‘*’);
 
Ввід - вивід у файл
 
Для зчитування та запису у файли використовують об‘єкти класу fstream.h , що є похідним
від класу iostream.h. Для запису у файл необхідно спочатку створити об‘єкт типу ofstream та
зв‘язати його з певним файлом на диску.
 
ofstream fout(“myfile1.cpp”);
 
Для зчитування з файлу створюється об‘єкт типу ifstream та в якості параметру передається ім‘я
файлу.
 
ifstream fin(“myfile2.cpp”);
 
fout  та fin це імена об‘єктів.
При кожному відкритті файлу для зчитування або запису створюється певний об‘єкт файлового
потоку. По завершенню роботи файл необхідно закрити з допомогою функції close().
Приклад:
 
#include <fstream.h>
#include <iostream.h>
 
void main()
{ char fileName[80];
   char buffer[255];
   cin >> fileName;  // ввести ім‘я файлу для виводу
   ofstream fout(fileName); // об‘єкт fout типу ofstream зв‘язується з файлом на
 диску
fout << “ABCD\n”; // запис у файл стрічки ABCD
cin.ignore(1,’\n’); // ігнорується символ кінця стрічки після імені файлу
cin.getline(buffer, 255);  // в масив buffer записується інформація
fout<< buffer << “\n”;  // з масиву запис у файл
fout.close( );  // закрити файл для виводу
 
ifstream fin(fileName); // відкрити той самий файл але для зчитування
char ch;
while (fin.get(ch)) // зчитати один символ
cout<< ch;  // вивести один символ на екран
fin.close( ); // закрити файл для зчитування
}
 
За замовчуванням вивід у існуючий файл починається з початку файлу, тобто попередня
інформація стирається. Додаткові аргументи конструктора дозволяють змінити стандартну
поведінку, а саме:
-         ios::app –додати дані в кінець файлу;
-         ios::ate – здійснює перехід в кінець файлу але дозволяє запис в будь-якому місці файлу;
-         ios::trunk – переходить на початок файлу;
-         ios::nocreate – відкрити існуючий файл;
-         ios::noreplace – відкрити неіснуючий файл.
Приклад:
 
ofstream fout(filename, ios::app);
 

You might also like