You are on page 1of 92

ЗМІСТ

ЗМІСТ..................................................................................................................................................1
ПЕРЕДМОВА......................................................................................................................................2
Лабораторна робота № 1 КЛАСИ І ОБ'ЄКТИ В С ++.....................................................................3
Лабораторна робота № 2 УСПАДКУВАННЯ І ПОЛІМОРФІЗМ.................................................19
Лабораторна робота № 3 ВІДНОШЕННЯ МІЖ КЛАСАМИ. ІЄРАРХІЯ ОБ’ЄКТІВ.
ІТЕРАТОРИ........................................................................................................................................30
Лабораторна робота № 4 ПЕРЕВАНТАЖЕННЯ ОПЕРАЦІЙ.......................................................42
Лабораторна робота № 5 ШАБЛОНИ ФУНКЦІЙ І КЛАСІВ........................................................53
Лабораторна робота № 6 КЛАСИ ПОТОКІВ.................................................................................61
Лабораторна робота № 7 СТАНДАРТНА БІБЛІОТЕКА ШАБЛОНІВ........................................75
СПИСОК ЛІТЕРАТУРИ....................................................................................................................92
ПЕРЕДМОВА

Мета практикуму  закріпити знання, отримані при вивченні теоретичної


частини курсу і отримати практичні навички розробки об'єктно-орієнтованих
програм. Практикум охоплює всі розділи об'єктно-орієнтованого програмування
на мові С++ і включає виконання восьми лабораторних робіт. Перші чотири
роботи пов'язані з базовими поняттями С++, такими як об'єкти і класи,
успадкування, поліморфізм і віртуальні методи, перевантаження операцій.
Останні чотири роботи присвячені професійному програмування на С++ і
охоплюють розділи професійного програмування, такі як шаблони, потокові
класи, стандартна бібліотека шаблонів і обробка подій.
Лабораторна робота № 1
КЛАСИ І ОБ'ЄКТИ В С ++

Мета. Отримати практичні навички реалізації класів на С ++.


Основний зміст роботи. Написати програму, в якій створюються і
знищуються об'єкти, визначеного користувачем класу. Виконати дослідження
викликів конструкторів і деструкторів.
Короткі теоретичні відомості.
Класи. Клас  фундаментальне поняття С++, він лежить в основі багатьох
властивостей С++. Клас надає механізм для створення об'єктів. У класі
відображені найважливіші концепції об'єктно-орієнтованого програмування:
інкапсуляція, уcпадкування, поліморфізм.
З погляду синтаксису, клас в С++  це структурований тип, утворений на
основі вже існуючих типів.
У цьому сенсі клас є розширенням поняття структури. У найпростішому
випадку клас можна визначити за допомогою конструкції:
тип_класу назву_класу
{
перелік_полів_класу
};
де, тип_класу – одне з службових слів class, struct, union; назву_класу –
ідентифікатор; перелік_полів_класу – визначення та опис типізованих даних і
методів, що належать класу.
Методи – це методи класу, які визначають операції над об'єктами. Дані –
це поля класу, що утворюють його структуру. Значення полів визначають стан
об'єкта.
Приклади.
struct date // Дата
{Int month, day, year; // Поля: місяць, день, рік
void set(int, int, int); // Метод – встановити дату
void get(int*, int*, int*); // Метод – отримати дату
void next(); // Метод – встановити наступну
дату
void print (); // Метод – вивести дату
};

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


{double re, im;
double real () {return (re);}
double imag () {return (im);}
void set (double x, double y) {re = x; im = y;}
void print() {cout<<"re="<<re; cout<<"im="<<Im;}
};
Для опису об'єкта класу (екземпляра класу) використовується конструкція:
назва_класу назва_об'єкта;
date today, my_birthday;
date *point = &Today; //Вказівник на об'єкт типу date
date clim [30]; // Масив об'єктів
date &name = my_birthday; // посилання на об'єкт
У об'єкти входять дані, відповідні полям класу. Методи класу дозволяють
обробляти дані конкретних об'єктів класу. Звертатися до даних об'єкта і
викликати методу для об'єкта можна двома способами. Перший за допомогою
"кваліфікованих" імен:
назва_об'єкта.назва_поля
назва_об'екта. назва_методу
Приклад:
complex x1, x2;
x1.re = 1.24;
x1.im = 2.3;
x2.set (5.1,1.7);
x1.print ();
Другий спосіб доступу використовує вказівник на об'єкт:
вказівник_на_об’єкт – > назва_компоненти
complex * point = & x1; // Або
point = new complex;
point – > re = 1.24;
poi nt – > im = 2.3;
point – > Print ();
Доступність компонентів класу. У розглянутих раніше прикладах класів
компоненти класів є загальнодоступними. У будь-якому місці програми, де
"видно" визначення класу, можна отримати доступ до компонентів об'єкта класу.
Тим самим не виконується основний принцип абстракції даних – інкапсуляція
(приховування) даних усередині об'єкта. Для зміни видимості компонент у
визначенні класу можна використовувати специфікатори доступу:
 public,
 private,
 protected.
Загальнодоступні (public) компоненти доступні в будь-якій частині
програми. Вони можуть використовуватися будь-якою методом як усередині
даного класу, так і поза ним. Доступ ззовні здійснюється через назву об'єкта:
назва_об'єкта.назва_компонента_класу
посилання_на_об'ект.назва_компонента_класса
вказівник_на_об’єкт– > назва_компонента_класса
Власні (private) компоненти локалізовані в класі і не доступні ззовні. Вони
можуть використовуватися методами даного класу і методами-"друзями" цього
класу, в якому вони описані.
Захищені (protected) компоненти доступні всередині класу і в похідних
класах.
Змінити статус доступу до компонентів класу можна і за допомогою
використання у визначенні класу ключового слова class. У цьому випадку всі
компоненти класу за замовчуванням є власними.
Приклад.
Class complex
{
double re, im; // Private по замовчуванням
public:
double real () {return re;}
double imag () {return im;}
void set (double x, double y) {re = x; im = y;}
};
Конструктори. Недоліком розглянутих раніше класів є відсутність
автоматичної ініціалізації створюваних об'єктів. Для кожного новостворюваного
об'єкта необхідно було викликати метод типу set (як для класу complex) або
явним чином присвоювати значення даним об'єкта. Однак для ініціалізації
об'єктів класу в його визначенні можна явно включити спеціальний метод,
званий конструктором. Формат визначення конструктора наступний:
назва_класу(список_форм_параметрів)
{ оператори_тіла_конструктору}
Назва цього методу за правилами мови С++ має збігатися з назвою класу.
Такий метод автоматично викликається при визначенні або розміщенні в пам'яті
за допомогою оператора new кожного об'єкта класу.
Приклад.
сomplex (double re1 = 0.0, double im1 = 0.0)
{
re = re1;
im = im1;
}
Конструктор виділяє пам'ять для об'єкта і ініціалізує поля класу.
Конструктор має ряд особливостей:
 для конструктора не визначається тип значення. Навіть тип void не
допустимо;
 вказівник на конструктор не може бути визначений, і не можна
отримати адресу конструктора;
 конструктори не успадковуються;
 конструктори не можуть бути описані з ключовими словами virtual,
static, const, mutuable, valatile.
 конструктор завжди існує для будь-якого класу, причому, якщо він
не визначений явно, він створюється автоматично. За умовчанням
створюється конструктор без параметрів і конструктор копіювання.
Якщо конструктор описаний явно, то конструктор за замовчуванням
не створюється. За замовчуванням конструктори створюються
загальнодоступними.
 параметром конструктора не може бути його власний клас, але може
бути посилання на нього (T &). Без явної вказівки програміста
конструктор завжди автоматично викликається при визначенні
(створенні) об'єкта. У цьому випадку викликається конструктор без
параметрів. Для явного виклику конструктора використовуються дві
форми:
назву_класу назва_об'єкта (фактичні_параметри);
назву_класу (фактичні_параметри);

 перша форма допускається тільки при не порожньому списку


фактичних параметрів. Вона передбачає виклик конструктора при
визначенні нового об'єкта даного класу:
complex ss (5.9,0.15);

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


complex ss = complex (5.9,0.15);
Існують два способи ініціалізації даних об'єкта за допомогою
конструктора. Раніше ми розглядали перший спосіб, а саме, передача значень
параметрів в тіло конструктора. Другий спосіб передбачає застосування списку
ініціалізаторів даного класу. Цей список розміщується між списком параметрів і
тілом конструктора. Кожен ініціалізатор списку належить до конкретного поля і
має вигляд:
назва_поля (вираз)
Приклади.
class CLASS_A
{
int i;
float e;
char c;
public:
CLASS_A (int а, float b, char s): i(8), e(i*b+a), с(s) {}
...
};

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


#include <string.h>
#include <iostream.h>
class string
{
char *ch; // Вказівник на символьний рядок
int len; // Довжина символьного рядка
public:
// конструктори
// Створює об'єкт – порожній рядок
string (int N = 80): len (0) {ch = new char [N +1]; ch [0] = '\ 0';}
// Створює об'єкт по заданому рядку
string (const char * arch)
{
len = strlen(arch);
ch = new char [len + 1];
strcpy (ch, arch);}
// Методи
// Повертає посилання на довжину рядки
int & len_str (void) {return len;}
// Повертає вказівник на рядок
char * str (void) {return ch;}
. . .};
Тут у класі string два конструктори – перевантажені. За замовчанням
створюється також конструктор копіювання виду:
T::T (const T &),
де, Т – назва класу.
Конструктор копіювання викликається щоразу, коли виконується
копіювання об'єктів, що належать класу. Зокрема він викликається:
а) коли об'єкт передається методу по значенню;
б) при побудові тимчасового об'єкта як значення, що повертає метод;
в) при використанні об'єкта для ініціалізації іншого об'єкта.
Якщо клас не містить явного конструктора копіювання, то при виникненні
однієї з цих трьох ситуацій відбувається побітовое копіювання об'єкта. Побітове
копіювання не у всіх випадках є адекватним. Саме для таких випадків і
необхідно визначати власний конструктор копіювання. Наприклад, в класі string:
string (const string & st)
{
len = strlen (st.len);
ch = new char [len + 1];
strcpy (ch, st.ch);
}
Можна створювати масив об'єктів, однак при цьому клас повинен мати
конструктор за замовчуванням (без параметрів). Масив об'єктів може
ініціалізуватися або автоматично конструктором за замовчуванням, або явним
присвоєнням значень кожному елементу масиву.
class demo
{
int x;
public:
demo () {x = 0;}
demo (int i) {x = i;}
};
void main () {
class demo a[20]; //виклик конструктора без параметрів (за
замовчуванням)
class demo b [2] = {demo (10), demo (100)}; // явне присвоювання
Деструктори. Динамічне виділення пам'яті для об'єкта створює
необхідність звільнення цієї пам'яті при знищенні об'єкта. Наприклад, якщо
об'єкт формується як локальний всередині блоку, то доцільно, щоб при виході з
блоку, коли вже об'єкт перестає існувати, виділена для нього пам'ять була
повернута. Бажано, щоб звільнення пам'яті відбувалося автоматично. Таку
можливість забезпечує спеціальний метод класу – деструктор класу. Його
формат:
~ назва_класу ()
{
оператори_тіла_деструктора
}
Назва деструктора збігається з назвою його класу, але передує символом
"~" (тильда). Деструктор не має параметрів і значення, що повертається. Виклик
деструктора виконується, не відкрито (автоматично), як тільки об'єкт класу
знищується. Наприклад, при виході за область визначення або при виклику
оператора delete для вказівника на об'єкт.
string * p = new string ("рядок");
delete p;
Якщо в класі деструктор не визначений явно, то компілятор генерує
деструктор за замовчуванням, який просто звільняє пам'ять, від даних об'єкта. У
тих випадках, коли потрібно виконати звільнення інших об'єктів пам'яті,
наприклад область, на яку вказує ch в об'єкті string, необхідно визначити
деструктор явно:
~ string () {delete [] ch;}
Так само, як і для конструктора, не може бути визначений вказівник на
деструктор.
Вказівники на методи. Можна визначити вказівник на методи.
тип_значення(назва_класу::*назва_вказ_на_метод)
(список_параметрів_методу);
Приклад.
double (complex:: * ptcom) (); // Визначення вказівника
ptcom = & complex::real; // Налаштування вказівника
// Тепер для об'єкта А можна викликати його метод
complex A (5.2,2.7);
cout << (A.*ptcom) ();
Можна визначити також тип вказівника на метод
typedef double & (complex :: * PF) ();
а потім визначити і сам вказівник:
PF ptcom = & complex :: real;
Порядок виконання роботи.
1. Побудувати діаграму та визначити користувацький клас у відповідності
з варіантом завдання (дивися додаток).
2. Визначити в класі наступні конструктори: без параметрів, з
параметрами, копіювання.
3. Визначити в класі деструктор.
4. Визначити в класі методи для перегляду і установки значень полів.
5. Визначити вказівник на метод.
6. Визначити вказівник на екземпляр класу.
7. Написати демонстраційну програму, в якій створюються і знищуються
об'єкти користувацького класу і кожен виклик конструктора і деструктора
супроводжується видачею відповідного повідомлення (який об'єкт, який
конструктор або деструктор викликав).
8. Показати в програмі використання вказівника на об'єкт і вказівника на
метод.
Методичні вказівки.
1. Діаграма класів будується за UML-нотацією або нотацією Г. Буча і
може бути подана так як показано на рис. 1

Рис. 1. Діаграма класу

В залежності від степені деталізації діаграми, опис поля може включати ім’я
поля, тип і значення, що присвоюється за замовчанням, а також ознаку
видимості:
< ознака видимості > < ім’я > : < тип > = < значення >
При описі методів вказують:
< ознака видимості > < ім’я > (< список параметрів >) : < тип результату >
Для полів та методів ознака видимості може приймати такі значення:
 символ ‘+’ – загальнодоступний;
 символ ‘#’ – захищений;
 символ ‘– ‘ – приватний.
Приклад визначення класу.
const int LNAME=25;
class STUDENT{
char name[LNAME]; // ім'я
int age; // вік
float grade; // рейтинг
public:
STUDENT(); //конструктор без параметрів
STUDENT(char*,int,float);//конструктор з параметрами
STUDENT(const STUDENT&); // конструктор копіювання
~STUDENT(); // деструктор
char* GetName() ;
int GetAge() const;
float GetGrade() const;
void SetName(char*);
void SetAge(int);
void SetGrade(float);
void Set(char*,int,float);
void Show(); };
Професійніше визначення поля name типу покажчик: char* name. Проте в
цьому випадку реалізація методів ускладнюється.
2. Приклад реалізації конструктора з видачею повідомлення.
STUDENT::STUDENT(char*NAME,int AGE,float GRADE)
{
strcpy(name,NAME);
age=AGE;
grade=GRADE);
cout<<\nКонструктор з параметрами, викликаний для об'єкту
<<this<<endl;
}
3. Слід передбачити в програмі всі можливі способи виклику конструктора
копіювання. Нагадуємо, що конструктор копіювання викликається:
а) при використанні об'єкту для ініціалізації іншого об'єкту
STUDENT a(“Петренко”,19,50), b=a;
б) коли об'єкт передається методу по значенню
void View(STUDENT a){a.Show;}
в) при побудові тимчасового об'єкту як значення методу , що повертається
STUDENT NoName(STUDENT & student)
{
STUDENT temp(student);
temp.SetName(“NoName”);
return temp;
}
STUDENT c=NoName(a);
4. У програмі необхідно передбачити розміщення об'єктів як в статичній,
так і в динамічній пам'яті, а також створення масивів об'єктів.
а) масив студентів розміщується в статичній пам'яті
STUDENT gruppa[3];
gruppa[0].Set(“Иванов”,19,50);
або
STUDENT gruppa[3] = { STUDENT(“Иванов”,19,50),
STUDENT(“Петрова”,18,25.5),
STUDENT(“Сидоров”,18,45.5)
};
б) масив студентів розміщується в динамічній пам'яті
STUDENT *p;
p=new STUDENT [3];
p-> Set(“Иванов”,19,50);
5. Приклад використання вказівника на метод
void (STUDENT::*pf)();
pf=&STUDENT::Show;
(p[1].*pf)();
6. Програма використовує три файли:
• заголовний h-файл з визначенням класу
• cpp-файл з реалізацією класу
• сpp-файл демонстраційної програми.
Для запобігання багатократному включенню файлу-заголовка слід
використовувати директиви препроцесора
#ifndef STUDENT.H
#define STUDENT.H
// модуль STUDENT.H
...
#endif
Зміст звіту.
1. Титульний лист: назва дисципліни; номер і найменування роботи;
прізвище, ім'я, по батькові студента; дата виконання.
2. Постановка завдання. Слід дати конкретну постановку, тобто вказати,
який клас повинен бути реалізований, які повинні бути в нім конструктори,
компоненти-методу і т.д.
3. Визначення призначеного для користувача класу з коментарями.
4. Реалізація конструкторів і деструктора.
5. Фрагмент програми, що показує використання вказівника на об'єкт і
вказівника на метод з поясненням.
6. Лістинг основної програми, в якому повинно бути вказано, в якому
місці і який конструктор або деструктор викликаються.
Додаток
Таблиця 1
Варіанти завдань
№ Назва класу Поле Тип поля
1. СТУДЕНТ ім’я char*
курс int
стать int(bool)
2. ВИРІБ назва char*
шифр char*
кількість int
3. АДРЕСА ім’я char*
вулиця char*
номер будинку int
4. ЦЕХ назва char*
начальник char*
кількість працівників int
5. КРАЇНА назва char*
форма управління char*
площа float
6. СЛУЖБОВЕЦЬ ім’я char*
вік int
стаж роботи int
7. БІБЛІОТЕКА назва char*
автор char*
ціна float
8. ТОВАР назва char*
кількість int
ціна float
9. ПЕРСОНА ім’я char*
вік int
стать int(bool)
10. ТВАРИНА назва char*
вид char*
средня вага int
11. КАДРИ ім’я char*
номер цеху int
разряд int
12. ЭКЗАМЕН ім’я студента char*
дата int
оцінка int
13. КВИТАНЦІЯ номер int
дата int
сума float
14. АВТОМОБІЛЬ марка char*
потужність float
ціна float
15. КОРАБЕЛЬ назва char*
тонажність int
тип char*
16. СТУДЕНТ ім’я char*
курс int
стать int(bool)
17. ВИРІБ назва char*
шифр char*
кількість int
18. АДРЕСА назва char*
вулиця char*
номер будинку int
19. ЦЕХ назва char*
начальник char*
кількість працівників int
20. КРАЇНА назва char*
форма управління char*
площа float
21. СЛУЖБОВЕЦЬ ім’я char*
вік int
стаж роботи int
22. БІБЛІОТЕКА назва char*
автор char*
ціна float
23. ТОВАР назва char*
кількість int
ціна float
24. ПЕРСОНА ім’я char*
вік int
стать int(bool)
25. ТВАРИНА назва char*
вид char*
средня вага int
26. КАДРИ ім’я char*
номер цеху int
разряд int
27. ЭКЗАМЕН ім’я студента char*
дата int
оцінка int
28. КВИТАНЦІЯ номер int
дата int
сума float
29. АВТОМОБІЛЬ марка char*
потужність float
ціна float
30. КОРАБЕЛЬ назва char*
тонажність int
тип char*
Лабораторна робота № 2
УСПАДКУВАННЯ І ПОЛІМОРФІЗМ

Мета. Отримати практичні навички створення ієрархії класів і


використання статичних компонентів класу.
Основний зміст роботи. Написати програму, в якій створюється ієрархія
класів. Включити поліморфні об'єкти в зв'язаний список, використовуючи
статичні компоненти класу. Показати використання віртуальних методів.
Короткі теоретичні відомості.
Статичні поля класу. Такі компоненти повинні бути визначені в
класі, як статичні (static). Статичні поля класів не дублюються при
створенні об'єктів, тобто кожен статичний компонент існує в єдиному
екземплярі. Доступ до статичного компоненту можливий тільки після його
ініціалізації. Для ініціалізації використовується конструкція

тип назву_класу::назва_поля ініціалізатор;


Наприклад, int complex::count = 0;
Цей запис має бути розміщено в глобальній області після визначення
класу. Тільки при ініціалізації статичне поле класу отримує пам'ять і стає
доступним. Звертатися до статичного поля класу можна звичайним чином через
назву об'єкта:
назва_об'екта.назва_поля
Але до статичних полів можна звертатися і тоді, коли об'єкт класу ще не
існує. Доступ до статичних полів можливий не тільки через назву об'єкта, але і
через назву класу:
назву_класу:: назва_поля
Однак так можна звертатися тільки до public полів.
Для звернення до private статичного поля ззовні можна за допомогою
статичних методів. Їх можна викликати через назву класу:
назву_класу:: назва_статичного_методу
Приклад.
#include <iostream.h> class TPoint
{
double x, y;
static int N; // Статичне поле: кількість точок
public:
TPoint (double x1 = 0.0, double y1 = 0.0) {N ++; x = x1; y = y1;}
static int& count () {return N;} // статичний метод
};
int TPoint:: N = 0; // ініціалізація статичного поля
void main (void)
{TPoint A (1.0, 2.0);
TPoint B (4.0, 5.0);
TPoint C (7.0, 8.0);
cout << \ nЗадано "<< TPoint::count () <<" точки "; }

Вказівник this. Коли метод класу викликається для обробки даних


конкретного об'єкту, цьому методу автоматично і неявно передається
вказівник на той об'єкт, для якого метод викликаний. Цей вказівник має
назву this і неявно визначений у кожному методі класу таким чином:
назву_класу * const this = адреса_об'єкту
Вказівник this є додатковим прихованим параметром кожного
нестатичного методу. При вході в тіло методу класу this ініціалізується
значенням адреси того об'єкта, для якого викликаний метод. У результаті цього
об'єкт стає доступним всередині цього методу .
У більшості випадків використання this є неявним. Зокрема, кожне
звернення до нестатичного методу класу неявно використовує this для доступу
до поля відповідного об'єкта.
Успадкування. Успадкування  це механізм отримання нового класу на
основі вже існуючого. Існуючий клас може бути доповнений або змінений для
створення нового класу.
Існуючі класи називаються базовими, а нові – похідними. Похідний клас
успадковує опис базового класу; потім він може бути змінений додаванням
нових полів, зміною існуючих методів і зміною прав доступу. За допомогою
успадкування може бути створена ієрархія класів, які спільно використовують
код і інтерфейси.
Успадковані компоненти не переносяться в похідний клас, а залишаються
в базових класах.
В ієрархії похідний об'єкт успадковує дозволені для наслідування
компоненти всіх базових об'єктів (public, protected).
Допускається множинне успадкування – можливість для деякого класу
успадковувати компоненти декількох ніяк не пов'язаних між собою базових
класів. В ієрархії класів угоду щодо доступності компонентів класу наступне:
private – компонент класу може використовуватися тільки методами
даного класу і методми – "друзями" свого класу. У похідних класі він
недоступний.
protected – те ж, що і private, але додатково компонент класу з даним
атрибутом доступу може використовувати методами і функціями-"друзями"
класів, похідних від даного.
public – компонент класу може використовуватися будь-яким методом
даного чи похідного класів, а також до public-компонентів можливий доступ
ззовні через назву об'єкта.
Слід мати на увазі, що оголошення friend не є атрибутом доступу і не
успадковується.
Синтаксис визначення похідного класу:
class назву_класу: список_базових_класів
{список_компонентів_класу};
У похідному класі успадковані компоненти отримують статус доступу
private, якщо новий клас визначений за допомогою ключового слова class, і
статус public, якщо за допомогою struct.
Явно змінити замовчуваний статус доступу при спадкуванні можна за
допомогою атрибутів доступу – private, protected і public, які вказуються
безпосередньо перед іменами базових класів.
Конструктори і деструктори похідних класів. Оскільки конструктори
успадковуються, при створенні похідного класу успадковані ним поля повинні
ініціалізуватися конструктором базового класу. Конструктор базового класу
викликається автоматично і виконується до конструктора похідного класу.
Параметри конструктора базового класу вказуються у визначенні конструктора
похідного класу. Таким чином відбувається передача аргументів від
конструктора похідного класса конструктору базового класу.
Наприклад.
Class Basis

{Int a, b;
public:
Basis (int x, int y) {a = x; b = y;}
};
class Inherit: public Basis
{int sum;
public:
Inherit (int x, int y, int s): Basis (x, y) {sum = s;}
};
Об'єкти класу конструюються знизу вгору: спочатку базовий, потім
компоненти-об'єкти (якщо вони є), а потім сам похідний клас. Таким чином,
об'єкт похідного класу містить як підоб'єкт об'єкт базового класу.
Знищуються об'єкти в зворотному порядку: спочатку похідний, потім його
компоненти-об'єкти, а потім базовий об'єкт.
Таким чином, порядок знищення об'єкта протилежний по відношенню до
порядку його конструювання.
Поліморфізм. Віртуальні методи. До механізму віртуальних методів
звертаються в тих випадках, коли в кожному похідному класі потрібно свій
варіант деякого методу. Класи, що включають такі методу, називаються
поліморфними і відіграють особливу роль у ООП.
Віртуальні методи надають механізм пізнього (відкладеного) або
динамічного зв'язування. Будь-який нестатичних метод базового класу може
бути віртуальним, для чого використовується ключове слово virtual.
Приклад.
Class base
{
public:
virtual void print () {cout << "\ nbase";}
...
};

class dir: public base


{
public:
void print () {cout << "\ ndir";}
};
void main ()
{
base B, * bp = & B;
dir D, * dp = & D;
base * p = & D;
bp – > print (); // Base dp – > print (); // Dir p – > print (); // dir

Таким чином, інтерпретація кожного виклику віртуального методу через


вказівник на базовий клас залежить від значення цього вказівника, тобто від
типу об'єкта, для якого виконується виклик.
Вибір того, який віртуальний метод викликати, буде залежати від типу
об'єкта, на який фактично (у момент виконання програми) спрямований
вказівник, а не від типу вказівника.
Віртуальними можуть бути тільки нестатичні методи.
Віртуальність успадковується. Після того як метод визначений як
віртуальний, його повторне визначення в похідному класі (з тим же самим
прототипом) створює в цьому класі новий віртуальний метод, причому
специфікатор virtual може не використовуватися.
Конструктори не можуть бути віртуальними, на відміну від деструкторів.
Практично кожен клас, що має віртуальний метод, повинен мати віртуальний
деструктор.
Абстрактні класи. Абстрактним називається клас, в якому є хоча б один
чисто (пустий) віртуальний метод.
Чисто віртуальним методом називається метод, який має наступне
визначення:
virtual тип назва_методу (список_формальних_параметрів) = 0;
Чисто віртуальний метод нічого не робить і недоступний для викликів.
Його призначення – служити інтерфейсом для реалізації методів в похідних
класах. Абстрактний клас може використовуватися тільки в якості базового для
похідних класів.
Механізм абстрактних класів розроблений для представлення загальних
понять, які в подальшому передбачається конкретизувати. При цьому побудова
ієрархії класів виконується за наступною схемою. На чолі ієрархії стоїть
абстрактний базовий клас. Він використовується для наслідування інтерфейсу.
Похідні класи будуть конкретизувати і реалізувати цей інтерфейс. В
абстрактному класі оголошені чисто віртуальні методи, які по суті є абстрактні
методи.
Приклад.
Class Base {public:
Base (); // конструктор по замовчуванням
Base (const Base &); // конструктор копіювання
virtual ~ Base (); // віртуальний деструктор
virtual void Show () = 0; // чисто віртуальний метод
// інші чисто віртуальні методи
protected: // захищені компоненти класу
private: // часто залишається порожнім,
// інакше буде заважати майбутнім
// розробкам
};
class Derived: virtual public Base {
public:
Derived (); // конструктор по замовчуванням

Derived (const Derived &); // конструктор копіювання


Derived (параметри); // конструктор з параметрами
virtual ~ Derived (); // віртуальний деструктор
void Show (); // перевизначення віртуального методу
// інші перевизначені віртуальні методу
// інші перевантажені операції
protected: // використовується замість private,
// якщо очікується успадкування
private: // використовується для деталей реалізації
};
Об'єкт абстрактного класу не може бути формальним параметром методу,
однак формальним параметром може бути вказівник на абстрактний клас. У
цьому випадку з'являється можливість передавати методу в якості фактичного
параметра значення вказівника на похідний об'єкт, замінюючи ним вказівник на
абстрактний базовий клас. Таким чином ми отримуємо поліморфні об'єкти.
Абстрактний метод може розглядатися як узагальнення перевизначення. В
обох випадках поведінку батьківського класу змінюється для нащадка. Для
абстрактного методу, однак, поведінка просто не визначена. Будь-яка поведінка
задається у похідному класі.
Одна з переваг абстрактного методу є чисто концептуальною: програміст
може подумки наділити потрібною дією абстракції як завгодно високого рівня.
Наприклад, для геометричних фігур ми можемо визначити метод Draw, який їх
малює: трикутник TTriangle, коло TCircle, квадрат TSquare. Ми визначимо
аналогичний метод і для абстрактного батьківського класу TGraphObject. Однак
такий метод не може виконувати корисну роботу, оскільки в класі TGraphObject
просто немає достатньої інформації для малювання чого б то не було. Проте
присутність методу Draw дозволяє зв'язати функціональність (малювання)
тільки один раз з класом TGraphObject, а не вводити три незалежні концепції
для підкласів TTriangle, TCircle, TSquare.
Є і друга, більш актуальна причина використання абстрактного методу. В
об'єктно-орієнтованих мовах програмування зі статичними типами даних, до
яких відноситься і С++, програміст може викликати метод класу, тільки якщо
компілятор може визначити, що клас дійсно має такий метод. Припустимо, що
програміст хоче визначити поліморфну змінну типу TGraphObject, яка буде в
різні моменти часу містити фігури різного типу. Це допустимо для поліморфних
об'єктів. Проте компілятор дозволить використовувати метод Draw для змінної,
тільки якщо він зможе гарантувати, що в класі змінної є цей метод. Приєднання
методу Draw до класу TGraphObject забезпечує таку гарантію, навіть якщо
метод Draw для класу TGraphObject ніколи не виконується. Природно, для того
щоб кожна фігура малювалася по-своєму, метод Draw повинен бути
віртуальним.
Порядок виконання роботи.
1. Визначити ієрархію класів (згідно з варіантом).
2. Визначити в класі статичне поле – вказівник на початок зв'язного
списку об'єктів і статичний метод для перегляду списку.
3. Реалізувати класи.
4. Написати демонстраційну програму, в якій створюються об'єкти різних
класів і розміщуються в список, після чого список проглядається.
5. Зробити відповідні методи не віртуальними і порівняти результат.
6. Реалізувати варіант, коли об'єкт додається до списку при створенні,
тобто в конструкторі.
Методичні вказівки.
1. Для визначення ієрархії класів зв'язати відношенням узагальнення
класи, наведені в додатку (для заданого варіанта). З перерахованих класів
вибрати один, який буде стояти на вершині ієрархії. Це абстрактний клас.
2. Визначити в класах всі необхідні конструктори і деструктори.
3. Поля класу специфікувати як protected.

4. Приклад визначення статичних полів:


static person * begin; // Вказівник на початок списку
static void print (void); // Перегляд списку
5. Статичне поле ініціалізувати поза межами класу, в глобальній області.
6. Щоб додати об'єкти в список передбачити метод класу, тобто об'єкт сам
додає себе в список. Наприклад,
a.Add () об'єкт a додає себе в список.

Включення об'єкта в список можна виконувати при створенні об'єкту,


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

результати будуть у разі віртуальних і не віртуальних методів.


Додаток. Варіанти завдань.
Перелік класів:
1) студент, викладач, персона, завкафедрою;
2) службовець, персона, робітник, інженер;
3) робочий, кадри, інженер, адміністрація;
4) деталь, механізм, виріб, вузол;
5) організація, страхова компанія, суднобудівна компа- нія, завод;
6) журнал, книга, друковане видання, підручник;
7) тест, іспит, випускний іспит, випробування;
8) місце, область, місто, мегаполіс;
9) іграшка, продукт, товар, молочний продукт;
10) квитанція, накладна, документ, чек;
11) автомобіль, поїзд, транспортний засіб, експрес;
12) двигун, двигун внутрішнього згоряння, дизель, турборе- активний
двигун;
13) республіка, монархія, королівство, держава;
14) ссавці, парнокопитні, птиці, тварина;
15) корабель, пароплав, вітрильник, корвет.
Лабораторна робота № 3
ВІДНОШЕННЯ МІЖ КЛАСАМИ. ІЄРАРХІЯ ОБ’ЄКТІВ. ІТЕРАТОРИ.

Мета. Отримати практичні навички створення об'єктів-груп і


використання методів-ітераторів.
Основні теоретичні відомості.
Група. Група  це об'єкт, який включає інші об'єкти. Об'єкти, що входять
в групу, називаються елементами групи. Елементи групи, у свою чергу, можуть
бути групою.
Приклади груп:
1. Вікно в інтерактивній програмі, яке володіє такими елементами, як поля
введення і редагування даних, кнопки, списки вибору, діалогові вікна і т.д.
2. Агрегат, що складається з більш дрібних вузлів.
3. Город, що складається з рослин, системи поливу та плану вирощування.
4. Якась організаційна структура (наприклад, факультет, кафедра,
студентська група).
Ми відрізняємо "групу" від "контейнера". Контейнер використовується
для зберігання інших даних. Приклади контейнерів: об'єкти класу TCollection і
об'єкти контейнерних класів бібліотеки STL в C++ (масиви, списки, черги). На
відміну від контейнера ми розуміємо групу як клас, який не тільки зберігає
об'єкти інших класів, але і володіє власними властивостями, які не випливають з
властивостей його елементів.
Група дає другий вид ієрархії (перший вид  ієрархія класів, побудована
на основі успадкування)  ієрархію об'єктів (ієрархію типу ціле/частина),
побудовану на основі агрегації.
Реалізувати групу можна декількома способами:
1. Клас "група" містить поля об'єктного типу. Таким чином, об'єкт "група"
в якості даних містить або безпосередньо свої елементи, або вказівники на них:
class TWindowDialog: public TGroup
{
protected:
TInputLine input1;
TEdit edit1;
TButton button1;
/* інші компоненти класу */
};
Такий спосіб реалізації групи використовується в C++ Builder.
2. Група містить поле last типу TObject *, який вказує на початок зв'язного
списку об'єктів, включених в групу. У цьому випадку об'єкти повинні мати поле
next типу TObject *, яке вказує на наступний елемент в списку.
3. Створюється зв'язаний список структур типу TItem:
struct TItem
{
TObject * item;
TItem * next;
};
Поле item вказує на об'єкт, включений у групу. Група містить поле last
типу TItem *, яке вказує на початок пов'язаного списку структур типу TItem.
Якщо необхідний доступ елементів групи до її полів і методів, об'єкт типу
TObject повинен мати поле owner типу TGroup *, яке вказує на власника цього
елемента.
Методи групи. Є два методи, які необхідні для функціонування групи:
1) void Insert (TObject * p); – вставляє елемент в групу.
2) void Show (); – дозволяє переглянути групу.
Крім цього група може містити наступні методи:
1) int Empty (); – показує, чи є хоча б один елемент в групі.
2) TObject * Delete (TObject * p); – видаляє елемент з групи, але зберігає
його в пам'яті.
3) void DelDisp (TObject * p); – видаляє елемент з групи і з пам'яті.
Ієрархія об'єктів. Ієрархія класів – це ієрархія за принципом
успадкування, тобто типу "це є різновид того". Наприклад, "робочий є різновид
персони", "автомобіль" є різновид "транспортного засобу". На відміну від цього
ієрархія об'єктів  це ієрархія за принципом входження, тобто типу "це є
частина того". Наприклад, "верстат  частину заводу", "двигун"  частина
"автомобіля". Таким чином, об'єкти нижнього рівня ієрархії включаються в
об'єкти більш високого рівня, які є для них групою.
Ітератори. Ітератори дозволяють виконувати деякі дії для кожного
елемента певного набору даних:
For all елементів набору {дії}
Такий цикл міг би бути виконаний для всього набору, наприклад, щоб
надрукувати всі елементи набору, або міг би шукати деякий елемент, який
задовольняє певній умові, і в цьому випадку такий цикл може закінчитися, як
тільки буде знайдений необхідний елемент.
Ми розглядатимемо ітератори як спеціальні методи класу-групи, що
дозволяють виконувати деякі дії для всіх об'єктів, включених в групу.
Прикладом ітератора є метод Show.
Нам би хотілося мати такий ітератор, який дозволяв би виконувати над
всіма елементами групи дії, задані не одним з методів об'єкта, а довільним
методом користувача. Такий ітератор можна реалізувати, якщо цей метод
передавати йому через вказівник на метод.
Визначимо тип вказівника на метод наступним чином:
typedef void (* PF) (TObject *, <додаткові параметри>);
Метод має обов'язковий параметр типу TObject або TObject *, через який
йому передається об'єкт, для якого необхідно виконати певні дії.
Метод-ітератор оголошується наступним чином:
void TGroup :: ForEach (PF action, <додаткові параметри>);

де action  єдиний обов'язковий параметр-вказівник на метод, який викликається


для кожного елемента групи; додаткові параметри  параметри, що
передаються методу, який викликається.
Потім визначається вказівник на метод і ініціалізується методом, що
передається ітератору:
PF pf = myfunc;
Тоді ітератор буде викликатися, наприклад, для додаткового параметра
типу int, так:
gr.ForEach(pf, 25);

де gr  об'єкт-група.
Динамічна ідентифікація типів.
Динамічна ідентифікація типу характерна для мов, в яких підтримується
поліморфізм. У цих мовах можливі ситуації, в яких тип об'єкта на етапі
компіляції невідомий.
У С ++ поліморфізм підтримується через ієрархії класів, віртуальні методи
і вказівники базових класів. При цьому вказівник базового класу може
використовуватися або для вказівки на об'єкт базового класу, або для вказівки на
об'єкт будь-якого класу, похідного від цього базового.
Нехай група містить об'єкти різних класів і необхідно виконати деякі дії
тільки для об'єктів певного класу. Тоді в ітераторі ми повинні розпізнавати тип
чергового об'єкта.
У стандарт мови С++ включені засоби RTTI (Run – Time Type
Idendification)  динамічна ідентифікація типів.
Інформацію про тип об'єкта отримують за допомогою оператора typeid,
визначення якого містить заголовний файл <typeinfo. h>.
Є дві форми оператора typeid:
typeid (об'єкт)
typeid (Назва_Типу)
Оператор typeid повертає посилання на об'єкт типу type_info. У класі
type_info перевантажені операції = = і ! =, що забезпечують порівняння типів.
Метод name() повертає вказівник на назву типу. Є одне обмеження.
Оператор typeid працює коректно тільки з об'єктами, у яких визначені віртуальні
методи . Більшість об'єктів мають віртуальні методи, хоча б тому, що зазвичай
деструктор є віртуальним для усунення потенційних проблем з похідними
класами. Коли оператор typeid застосовують до неполіморфному класу (в класі
немає віртуальної методу ), отримують вказівник або посилання базового типу.
Приклади.
1.
#include <iostream.h>

#include <typeinfo.h>
class Base {
virtual void f() {};
// ...
};
class Derived: public Base {
// ...
};
void main ()
{int i;
Base ob, * p;
Derived ob1;
cout << typeid(i) .name (); // Виводиться int
p = & ob1;
cout << typeid (*p).name(); // Виводиться Derived
}
2.
// початок див. вище
void WhatType (Base & ob)
{cout << typeid(ob).name() << endl;
}
void main ()
{
Base ob;
Derived ob1;
WhatType (ob); // Виводиться Base
WhatType (ob1); // Виводиться Derived
}
3.
// початок див. вище
void main ()
{
Base * p;
Derived ob;
p = & ob;
if (typeid (*p) == typeid (Derived)) cout << "p вказує на об'єкт типу
Derived ";
...
}
Якщо при зверненні typeid (*p), p = NULL, то збуджується виняткова
ситуація bad_typeid.
Порядок виконання роботи.
1. Доповнити ієрархію класів лабораторної роботи № 2 класами "Група".
Наприклад, для предметної області ФАКУЛЬТЕТ можна використати класи
"факультет", "студентська група", "кафедра". Рекомендується створити
абстрактний клас  "підрозділ", який буде базовим для всіх груп і абстрактний
клас TObject, що знаходиться на чолі всієї ієрархії.
2. Написати для класу-групи метод-ітератор.
3. Написати процедуру або метод, яка виконується для всіх об'єктів, що
входять в групу (дивися приклади в додатку).
4. Написати демонстраційну програму, в якій створюються,
відображаються і знищуються об'єкти-групи, а також демонструється
використання ітератора.
Методичні вказівки.
1. Клас-група повинен відповідати ієрархії класів лабораторної роботи
№2, тобто об'єкти цих класів повинні входити в групу. Наприклад, для
варіанта 1 може бути запропонована наступна ієрархія класів:

Tobject (абстр. клас)

Персона (абстр. клас) Підрозділ (абстрактна група)

Студент Викладач Факультет Кафедра Студентська


група

Зав. кафедрою

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


Факультет

Студ_группа_1 Студ_группа_N Кафедра_1 Кафедра_ N

Викладачі Викладачі
Студенти Студенти
Завкафедрою Завкафедрою

2. Для включення об'єктів в групу необхідно використовувати третій


спосіб (через зв'язаний список структур типу TItem).
3. Приклад визначення доданих абстрактних класів:
class TObject
{
public:
virtual void Show () = 0;};

class TDepartment: public TObject // абстрактний клас-група


{
protected:
char name [20]; // назвая

TPerson * head; // керівник


TItem * last; // Вказівник на початок зв'язаного списку структур TItem
public:
TDepartment (char *, TPerson *);
TDepardment (TDepartment &);
~ TDepartment ();
char * GetName ();
TPerson * GetHead ();
void SetName (char * NAME);
void SetHead (TPerson * p);
void Insert (TObject * p);
virtual void Show () = 0;
};

4. Ієрархія об'єктів створюється наступним чином (на прикладі


ФАКУЛЬТЕТУ):
а) створюється порожній ФАКУЛЬТЕТ,
б) створюється порожня КАФЕДРА,
в) створюються ВИКЛАДАЧІ і включаються до кафедр,
г) КАФЕДРА включається в ФАКУЛЬТЕТ,
д) теж повторюється для іншої кафедри,
е) створюється порожня СТУДЕНТСЬКА ГРУПА,
ж) створюються СТУДЕНТИ і включаються в Студентських груп,
з) СТУДЕНТСЬКА ГРУПА включається в ФАКУЛЬТЕТ,
і) теж повторюється для іншої студентської групи.
5. Видаляється ФАКУЛЬТЕТ (при виклику деструктора) у зворотному
порядку.
6. Метод-ітератор визначається в неабстрактних класах-групах на основі
вибраних запитів.
Наприклад, для класу TStudentGroup може бути запропонований ітератор:
void TStudentGroup :: ForEach (PF action, float parametr);
де action  вказівник на метод, який повинен бути виконаний для всіх об'єктів,
включених до групи (в даному випадку для всіх студентів), parametr – передана
процедурі додаткова інформація.
В якості переданої методу функції може бути запропонована, наприклад,
така: вивести список студентів, які мають рейтинг не нижче заданого
void MyProc (TObject * p, float rate)
{
if (((TStudent *) p) – > GetGrade ()> = rate) cout << (((TStudent *) p) – >
GetName ());
}
7. Студент визначає передану ітераторам функцію на основі запитів, які
повинні бути виконані викликом ітератора. Варіанти запитів наведені в додатку.
Зміст звіту.
1. Титульний лист.
2. Постановка завдання.
3. Ієрархія класів.
4. Ієрархія об'єктів.
5. Визначення класів (доданих або змінених в лабораторну роботою № 2).
6. Реалізація для жодного не абстрактного класу-групи всіх методів.
7. Реалізація ітератора.
8. Реалізація переданої ітератору функції .
9. Лістинг демонстраційної програми.
Додаток. Варіанти запитів.
1. Імена всіх осіб чоловічої (жіночої) статі.
2. Імена студентів зазначеного курсу.
3. Імена та посаду викладачів зазначеної кафедри.
4. Імена службовців зі стажем не менше заданого.
5. Імена службовців заданої професії.
6. Імена робочих заданого цеху.
7. Імена робочих заданої професії.
8. Імена студентів, що здали всі (заданий) іспити на відмінних але (добре і
відмінно).
9. Імена студентів, що не здали всі (хоча б один) іспит.
10. Імена всіх монархів на заданому континенті.
11. Найменування всіх деталей (вузлів), що входять в заданий вузол
(механізм).
12. Найменування всіх книг в бібліотеці (магазині), що вийшли не раніше
зазначеного року.
13. Назви всіх міст заданої області.
14. Найменування всіх товарів в заданому відділі магазину.
15. Кількість чоловіків (жінок).
16. Кількість студентів на зазначеному курсі.
17. Кількість службовців зі стажем не менше заданого.
18. Кількість робочих заданої професії.
19. Кількість інженерів в заданому підрозділі.
20. Кількість товару заданого найменування.
21. Кількість студентів, що здали всі іспити на відмінно.
22. Кількість студентів, що не здали хоча б один іспит.
23. Кількість деталей (вузлів), що входять в заданий вузол (механізм).
24. Кількість зазначеного транспортного засобу в автопарку (на
автостоянці).
25. Кількість пасажирів у всіх вагонах експреса.
26. Сумарна вартість товару заданого найменування.
27. Середній бал за сесію заданого студента.
28. Середній бал по предмету для всіх студентів.
29. Сумарна кількість підручників у бібліотеці (магазині).
30. Сумарна кількість жителів усіх міст в області.
31. Сумарна вартість продукції заданого найменування за всіма
накладними.
32. Середня потужність всіх (заданого типу) транспортних засобів у
організації.
33. Середня потужність всіх дизелів, обслуговуваних заданої фірмою.
34. Середня вага тварин заданого виду в зоопарку.
35. Середня водотоннажність всіх вітрильників на верфі (в порту).
Лабораторна робота № 4
ПЕРЕВАНТАЖЕННЯ ОПЕРАЦІЙ
Мета. Отримати практичні навички створення абстрактних типів даних
і перевантаження операцій в мові С ++.
Основний зміст роботи. Визначити і реалізувати клас абстрактний
тип даних. Визначити і реалізувати операції над даними цього класу.
Написати і виконати програми повного тестування цього класу.
Короткі теоретичні відомості.
Абстрактний тип даних (АТД). АТД  тип даних, який визначається
тільки через операції, які можуть виконуватися над відповідними об'єктами
безвідносно до способу подання цих об'єктів. АТД включає в себе абстракцію
як через параметризацію, так і через специфікацію. Абстракція через
параметризацію може бути здійснена так само, як і для процедур (методів);
використанням параметрів там, де це має сенс. Абстракція через
специфікацію досягається за рахунок того, що операції представляються як
частина типу.
Перевантаження операцій. Можливість використовувати знаки
стандартних операцій для запису виразів як для вбудованих, так і для АТД. У
мові С++ для перевантаження операцій використовується ключове слово
operator, за допомогою якого визначається спеціальна операція-метод
(operator function).
Формат операції-методу :
тип_поверт_знач operator знак_операції (спеціф_параметрів)
{оператори_тіла_методу}

Перевантаження унарних операцій Будь-яка унарна операція  може


бути визначена двома способами: або як компонентний метод без параметрів,
або як глобальна (можливо дружня) функція з одним параметром. У першому
випадку вираз Z означає виклик Z.operator (), у другому  виклик
operator (Z).
Унарні операції, перевантажуванні в рамках певного класу, можуть
перевантажуватися тільки через нестатичні компонентні методи без
параметрів. об'єкт класу, що викликає метод, автоматично сприймається як
операнд.
Унарні операції, перевантажуванні поза областю класу (як глобальні
методу ), повинні мати один параметр типу класу. Передаваний через цей
параметр об'єкт сприймається як операнд.
Синтаксис:
а) у першому випадку (опис в області класу):
тип_поверт_знач operator знак_операції
б) в другому випадку (опис поза областю класу):
тип_поверт_знач operator знак_операції (ідентифікатор_типу)
Приклади.
1) class person { 2) class person {
int age; int age;
... ...
public: public:
... ...
void operator ++ () { friend void operator ++ (person &);
++ age; };
} void person::operator++(person
&ob)
}; {++ ob.age;
}

void main () {
class person jon;
++ jon;
}
Перевантаження бінарних операцій. Будь-яка бінарна операція 
може бути визначена двома способами: або як компонентний метод з одним
параметром, або як глобальна (можливо дружня) функція з двома
параметрами. У першому випадку xy означає виклик x.operator (y), у
другому – виклик operator (x, y).
Операції, що перевантажуються всередині класу, можуть
перевантажуватися тільки нестатичними компонентними методами з
параметрами. Об'єкт класу автоматично сприймається в якості першого
операнду.
Операції, що перевантажуються поза областю класу, повинні мати два
операнди, один з яких повинен мати тип класу.
Приклади.
1). class person {...};
class adresbook {
// Містить як компонентні дані множину об'єктів типу
// person, що представляються як динамічний масив, список або дерево
person *M;
public:
person & operator [] (int); // доступ до i-го об'єкту
};
person & adresbook::operator [] (int i) {
return M[i]
}
void main (){
class adresbook persons;
class person record;
...
record = persons [3];
}
2). class person {...};
class adresbook {
// Містить як компонентні дані множину об'єктів типу
// person, що представляються як динамічний масив, список або дерево
...
public:
friend person & operator [] (const adresbook &, int); // доступ до i-го
об'єкту
};
person & operator [] (const adresbook & ob, int i) {
...
}
void main (){
class adresbook persons;
class person record;
...
record = persons [3];
}

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


трьома особливостями:
операція не успадковується;
операція визначена за замовчуванням для кожного класу в качест- ве
операції порозрядного копіювання об'єкта, що стоїть праворуч від знака
операції, в об'єкт, що стоїть зліва.
операція може перевантажуватися тільки в області визначення класу.
Це гарантує, що першим операндом завжди буде леводопус- тімое вираз.
Формат перевантаженої операції присвоювання:
назву_класу & operator = (назву_класу &);
Відзначимо дві важливі особливості методу operanor =. По-перше, в
ній використовується параметр-посилання. Це необхідно для запобігання
створення копії об'єкта, який передається через параметр за значенням. У
випадки створення копії, вона віддаляється викликом деструктора при
завершенні роботи методу. Але деструктор звільняє розподілену пам'ять, ще
необхідну об'єкту, який є аргументом. Параметр-посилання допомагає
вирішити цю проблему.
По-друге, метод operator=() повертає не об'єкт, а посилання на нього.
Сенс цього той же, що і при використанні параметра-посилання. Метод
повертає тимчасовий об'єкт, який видаляється після завершення його роботи.
Це означає, що для тимчасової змінної буде викликаний деструктор, який
звільняє розподілену пам'ять. Але вона необхідна для присвоювання значення
об'єкту. Тому, щоб уникнути створення тимчасового об'єкта, в якості
значення, що повертається викоистовується посилання.
Порядок виконання роботи.
1. Вибрати клас АТД відповідно до варіантом.
2. Визначити і реалізувати в класі конструктори, деструктор, методи
Input (введення з клавіатури) і Print (виедення на екран), перевантажити
операцію присвоювання.
3. Написати програму тестування класу і виконати тестування.
4. Доповнити визначення класу заданими перевантаженими операціями
(відповідно до варіантом).
5. Реалізувати ці операції. Виконати тестування.
Методичні вказівки.
1.Класс АТД реалізувати як динамічний масив. Для цього визначення
класу повинно мати такі поля:
 вказівник на початок масиву;
 максимальний розмір масиву;
 поточний розмір масиву.
2. Конструктори класу розміщують масив в пам'яті і встановлюють його
максимальний і поточний розмір. Для задання максимального розміру масиву
використовувати константу, яка визначається поза класом.
3. Щоб у вас не виникало проблем, акуратно працюйте з константними
об'єктами. Наприклад:
- конструктор копіювання слід визначити так: MyClass (const MyClass
& ob);
- операцію присвоювання перевантажити так: MyClass & operator =
(const MyClass & ob);
4. Для зручності реалізації операцій-методів реалізувати в класі private
(protected), що працюють безпосередньо з реалізацією класу. Наприклад, для
класу множина це можуть бути наступні методи :
 включити елемент в множину;
 знайти елемент і повернути його індекс;
 видалити елемент;
 визначити, чи належить елемент множині.
Зазначені методи використовуються в реалізації загальнодоступних
методів-операцій (operator).
Зміст звіту.
1. Титульний лист.
2. Конкретне завдання із зазначенням номера варіанту, реалізованого
класу і операцій.
3. Визначення класу.
4. Обґрунтування включення в клас декількох конструкторів,
деструктора та операції присвоювання.
5. Пояснити обрання типу пам'яті для об'єктів класу.
6. Реалізація перевантажених операцій з обґрунтуванням обраного
способу (метод класу, зовнішня метод, зовнішня дружня функція).
7. Тестові дані та результати тестування.
Питання для самоконтролю.
1. Що таке абстрактний тип даних?
2. Наведіть приклади абстрактних типів даних.
3. Які синтаксис/семантика "операції-методу "?
4. Як можна викликати операцію-метод?
5. Чи потрібно перевантажувати операцію присвоювання щодо
визначеного користувачем типу даних, наприклад класу? Чому?
6. Чи можна змінити пріоритет перевантаженої операції?
7. Чи можна змінити кількість операндів перевантаженої операції?
8. Чи можна змінити асоціативність перевантаженої операції?
9. Чи можна, використовуючи дружню метод, перевантажити оператор
присвоювання?
10. Чи всі оператори мови С ++ можуть бути перевантажені?
11. Якими двома різними способами визначаються перевантажені
операції?
12. Чи всі операції можна перевантажити за допомогою глобальної
дружної функції ?
13. У яких випадках операцію можна перевантажити тільки глобальною
функцією?
14. У яких випадках глобальна операція-функція повинна бути
дружньою?
15. Чи обов'язковий у методі operator параметр типу "клас" або
"Посилання на клас"?
16. Чи успадковуються перевантажені операції?
17. Чи можна повторно перевантажити в похідному класі операцію,
перевантажену в базовому класі?
18. У чому відмінність синтаксису операції-методу унарної і бінарної
операції?
19. Наведіть приклади перевантаження операцій для стандартних типів.
20. Перевантажите операцію "+" для класу "комплексне число".
21. Перевантажите операції "<", ">", "==" для класу "рядок символів
Додаток.
Варіанти завдань.
1. АТД  множина з елементами типу char. Додатково перевантажити
наступні операції:
+  додати елемент в множину (типу char + set);
+  об'єднання множин;
==  перевірка множин на рівність.
2. АТД  множина з елементами типу char. Додатково перевантажити
наступні операції:
-  видалити елемент з множини (типу set – char);
*  перетин множин;
== порівняння множин.
3. АТД  множина з елементами типу char. Додатково пере- вантажити
наступні операції:
-  видалити елемент з множиниі (типу set – char);
>  перевірка на підмножину;
! =  перевірка множин на нерівність.
4. АТД  множина з елементами типу char. Додатково
перевантажити наступні операції:
+  додати елемент в множину (типу set + char);
*  перетин множин;
int ()  потужність множини.
5. АТД  множина з елементами типу char. Додатково перевантажити
наступні операції:
()  конструктор множини;
+  об'єднання множин;
<=  порівняння множин .
6. АТД  множина з елементами типу char. Додатково перевантажити
наступні операції:
>  перевірка на приналежність (char in set);
*  перетин множин;
<  перевірка на підмножину.
7. АТД  однозв’язний список з елементами типу char. Додатково
перевантажити наступні операції:
+ – Об'єднати списки (list + list);
- – Видалити елемент з початку (типу – list);
== – Перевірка на рівність.
8. АТД  однозв’язний список з елементами типу char. Додатково
перевантажити наступні операції:
+ – Додати елемент в початок (char + list);
– – Видалити елемент з початку (типу – list);
== – Перевірка на рівність.
9. АТД  однозв’язний список з елементами типу char. Додатково
перевантажити наступні операції:
+  додати елемент в кінець (list + char);
--  видалити елемент з кінця (типу list --);
! =  перевірка на нерівність.
10. АТД  однозв’язний список з елементами типу char. Додатково
перевантажити наступні операції:
[]  доступ до елементу в заданій позиції, наприклад: int i; char c; list L;
c = L [i];
+  об'єднати два списку;
==  перевірка на рівність.
11. АДТ  однозв’язний список з елементами типу char. Додатково
перевантажити наступні операції:
()  видалити елемент в заданій позиції, наприклад: int i; list L; L (i);
()  додати елемент в задану позицію, наприклад: int i; char c; list L; L
(с, i);
! =  перевірка на нерівність.
12. АДТ  одновимірний масив (вектор) дійсних чисел. Додатково
перевантажити наступні операції:
+  складання векторів (a [i] + b [i] для всіх i);
[]  доступ по індексу;
+  додати число до вектора (double + vector).
13. АТД  одновимірний масив (вектор) дійсних чисел. Додатково
перевантажити наступні операції:
-  віднімання векторів (a [i] – b [i] для всіх i);
[]  доступ по індексу;
-  відняти з вектора число (vector – double).
14. АТД  одновимірний масив (вектор) дійсних чисел. Додатково
перевантажити наступні операції:
*  множення векторів (a [i] * b [i] для всіх i);
[]  доступ по індексу;
*  множення вектора на число (vector * double).
15. АТД  одновимірний масив (вектор) дійсних чисел. Додатково
перевантажити наступні операції:
int ()  розмір вектора;
()  встановити новий розмір;
-  відняти з вектора число (vector – double);
[] доступ по індексу;
16. АТД  одновимірний масив (вектор) дійсних чисел. Додатково
перевантажити наступні операції:
=  присвоїти всім елементам вектора значення (vector = double);
[]  доступ по індексу;
==  перевірка на рівність;
! =  перевірка на нерівність;
17. АТД  двовимірний масив (матриця) дійсних чисел. Додатково
перевантажити наступні операції:
[]  доступ по індексу;
*  множення матриць;
*  множення матриці на число;
*  множення числа на матрицю.
18. АТД  двовимірний масив (матриця) дійсних чисел. Додатково
перевантажити наступні операції:
()  доступ по індексу;
-  різниця матриць;
-  відняти з матриці число;
==  перевірка матриць на рівність.
19. АТД  двовимірний масив (матриця) дійсних чисел. Додатково
перевантажити наступні операції:
()  доступ по індексу;
=  присвоїти всім елементам матриці значення (matr = double);
+  додавання матриць;
+  скласти матрицю з числом (matr + double).
20. АТД  двовимірний масив (матриця) дійсних чисел. Додатково
перевантажити наступні операції:
()  доступ по індексу;
==  перевірка матриць на рівність;
++  транспонувати матрицю.
Лабораторна робота № 5
ШАБЛОНИ ФУНКЦІЙ І КЛАСІВ

Мета. Отримати практичні навички створення шаблонів і використання


їх у програмах С++.
Основний зміст роботи. Створити шаблон заданого класу і
використовувати його для даних різних типів.
Короткі теоретичні відомості
Шаблон функції. Шаблон функції (інакше параметризована функція)
визначає загальний набір операцій (алгоритм), які будуть застосовуватися до
даних різних типів. При цьому тип даних, над якими функція повинна
виконувати операції, передається їй у вигляді параметра на стадії компіляції.
В С++ параметризована функція створюється за допомогою ключового
слова template. Формат шаблону функції:
template <class тип_даних> тип_поверт_значення назва_функції
(список_параметрів) {тіло_функціі}
Основні властивості параметрів шаблону функції:
 імена параметрів шаблону повинні бути унікальними в усьому
визначеному шаблоні;
 список параметрів шаблону не може бути порожнім;
 у списку параметрів шаблону може бути кілька параметрів, і кожному
з них повинно передувати ключове слово class;
 назва параметру шаблону має всі права імені типу у визначеній
шаблоном функції;
 певна функція на основі шаблону може мати будь-яку кількість
непараметризованих формальних параметрів; може бути непараметризоване
значення, що повертається функцією;
 в списку параметрів сигнатури шаблону назви можуть не збігатися з
назвами тих же параметрів у визначенні шаблону;
 при конкретизації параметризованої функції необхідно, щоб при
виклику функції типи фактичних параметрів, відповідали параметризованим
формальним параметрам.
Шаблон класу. Шаблон класу (інакше параметризований клас)
використовується для побудови родового класу. Створюючи родовий клас, ви
створюєте ціле сімейство споріднених класів, які можна застосовувати до
будь-якого типу даних. Таким чином, тип даних, яким оперує клас, вказується
як параметр при створенні об'єкту, що належить до цього класу. Подібно до
того, як клас визначає правила побудови і формат окремих об'єктів, шаблон
класу визначає спосіб побудови окремих класів. У визначенні класу, що
входить в шаблон, назва класу є не назвою окремого класу, а
параметризованою назвою сімейства класів.
Загальна форма оголошення параметризованого класу:
template <class тип_даних> class назва_класу {... };
Основні властивості шаблонів класів:
 методи параметризованого класу автоматично є параметризованими.
Їх не обов'язково оголошувати за допомогою template.
 дружні функції, які описуються в параметризованому класі, не є
автоматично параметризованими функціями, тобто за замовчуванням
такі функції є дружніми для всіх класів, які організуються з даного
шаблону;
 якщо friend-функція містить у своєму описі параметр типу
параметризованого класу, то для кожного створеного за даним
шаблоном класу є власна friend-функція;
 в рамках параметризованого класу можна визначити friend-шаблони
(дружні параметризовані класи);
 з одного боку, шаблони можуть бути похідними (унаслідуватися) як
від шаблонів, так і від звичайних класів, з іншого боку, вони можуть
використовуватися в якості базових для інших шаблонів або класів;
 шаблони методів, не можна описувати як virtual;
 локальні класи не можуть містити шаблони в якості своїх елементів.
Методи параметризованих класів. Реалізація методу шаблону класу
поза визначення класу, повинна включати такі додаткові два елементи:
– визначення повинно починатися з ключового слова template, за яким
слідує такий же список_параметрів_типів в кутових дужках, який
використано у визначенні шаблону класу.
– за назвою_класу, перед операцією дозволу доступу ::, повинен
слідувати список_імен_параметрів шаблону.
template <список_типів> тип_поверт_значення назва_класу
<список_імен_параметрів>:: назва_функціі (список_параметрів) {... }
Порядок виконання роботи.
1. Створити шаблон заданого класу. Визначити конструктори,
деструктор, перевантажену операцію присвоювання ("=") і операції, задані в
варіанті завдання.
2. Написати програму тестування, в якій перевіряється використання
шаблону для стандартних типів даних.
3. Виконати тестування.
4. Визначити клас, який буде використовуватися як параметр шаблону.
Визначити в класі необхідні функції і перевантажені операції.
5. Написати програму тестування, в якій перевіряється використання
шаблону для типу визначеного класу.
6. Виконати тестування.
Методичні вказівки.
1. Клас АТД реалізувати як динамічний масив. Для цього визначення
класу повинно мати такі поля:
 вказівник на початок масиву;
 максимальний розмір масиву;
 поточний розмір масиву.
2. Для введення і виведення визначити в класі функції input() і print().
3. Щоб у вас не виникало проблем, акуратно працюйте з константними
об'єктами. наприклад:
– конструктор копіювання слід визначити так:
MyTmp (const MyTmp & ob);
– операцію присвоювання перевантажити так:
MyTmp & operator = (const MyTmp & ob);
4. Для шаблонів множин, списків, стеків і черг в якості стандартних
типів використовувати символьні, цілі і дійсні типи. Для користувацького
типу взяти клас з лабораторної роботи № 1.
5. Для шаблонів масивів в якості стандартних типів використовувати
цілі і дійсні типи. Для користувацького типу взяти клас "Комплексне число"
complex.
class complex {
int re; // дійсна частина
int im; // уявна частина
public;
// Необхідні методи і перевантажені операції
};
6. Реалізацію шаблону слід розмістити разом з визначенням в
заголовному файлі.
7. Тестування повинне бути виконане для всіх типів даних і для всіх
операцій.
Зміст звіту.
1. Титульний лист: назва дисципліни, номер і назва роботи, прізвище,
назва, по батькові студента, дата виконання.
2. Постановка завдання. Слід дати конкретну постановку, тобто вказати
шаблон якого класу повинен бути створений, які повинні бути в ньому
конструктори, методи, перевантажені операції і т.д. Те ж саме слід вказати
для користувацького класу.
3. Визначення шаблону класу з коментарями.
4. Визначення користувацького класу з коментарями.
5. Реалізація конструкторів, деструктора, операції присвоювання і
операцій, які задані в варіанті завдання.
6. Те ж саме для призначеного для класу користувача.
7. Результати тестування. Слід вказати для яких типів і які операції
перевірені і які виявлені помилки (або не виявлені)
Питання для самоконтролю.
1. У чому сенс використання шаблонів?
2. Які синтаксис/семантика шаблонів функцій?
3. Які синтаксис/семантика шаблонів класів?
4. Напишіть параметризовану функцію сортування масиву методом
обміну.
5. Визначте шаблон класу "вектор"  одновимірний масив.
6. Що таке параметри шаблону функції?
7. Перерахуйте основні властивості параметрів шаблону функції.
8. Як записувати параметр шаблону?
9. Чи можна перевантажувати параметризовані функції?
10. Перерахуйте основні властивості параметризованих класів.
11. Чи може бути порожнім список параметрів шаблону? Поясніть.
12. Як викликати параметризовану функцію без параметрів?
13. Чи всі методи параметризованого класу є параметризованими?
14. Чи є дружні функції, описані в параметризованому класі,
параметризованими?
15. Чи можуть шаблони класів містити віртуальні методи?
16. Як визначаються методи параметризованих класів поза визначення
шаблону класу?
Варіанти завдань.
1.Класс  одновимірний масив. Додатково перевантажити наступні
операції:
*  множення масивів;
[]  доступ за індексом.
2. Клас  одновимірний масив. Додатково перевантажити наступні
операції:
int ()  розмір масиву;
[]  доступ по індексом.
3. Клас  одновимірний масив. Додатково перевантажити наступні
операції:
[]  доступ за індексом;
==  перевірка на рівність;
! =  перевірка на нерівність.
4. Клас  множина set. Додатково перевантажити наступні операції:
+  додати елемент в множина (типу set + item);
+  об'єднання множин;
*  перетин множин;
5. Клас  множина set. Додатково перевантажити наступні операції:
+  додати елемент в множина (типу item + set);
+  об'єднання множин;
==  перевірка множин на рівність.
6. Клас  множина set. Додатково перевантажити наступні операції:
-  видалити елемент з множини (типу set – item);
*  перетин множин;
==  порівняння множин.
7. Клас  множина set. Додатково перевантажити наступні операції:
-  видалити елемент з множини (типу set – item);
>  перевірка на підмножину;
! =  перевірка множин на нерівність.
8. Класс  множина set. Додатково перевантажити наступні операції:
+  додати елемент в множину (типу set + item);
*  перетин множин;
int ()  потужність множини.
9. Клас  множина set. Додатково перевантажити наступні операції:
()  ініціалізатор множини;
+  об'єднання множин;
<=  порівняння множин.
10. Клас  множина set. Додатково перевантажити наступні операції:
>  перевірка на приналежність;
*  перетин множин;
< – перевірка на підмножину.
11. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
+  додати елемент в початок (List + item);
-  видалити елемент з початку (- List);
==  перевірка на рівність.
12. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
+  додати елемент в початок (item + list);
–  видалити елемент з початку (–list);
! =  перевірка на нерівність.
13. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
+  додати елемент в кінець (list + item);
-  видалити елемент з кінця (типу list–);
! =  перевірка на нерівність.
14. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
[]  доступ до елементу в заданій позиції, наприклад: type c; int i; list L;
c = L[i];
+  об'єднати два списку;
==  перевірка на рівність.
15. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
[]  доступ до елементу в даній точці, наприклад: int i; Type c; list L; c =
L [i];
+  злити два списку;
! =  перевірка на нерівність.
16. Клас  однозв’язний список list. Додатково перевизначити наступні
операції:
()  видалити елемент в заданій позиції, наприклад: int i; list L, L(i);
()  додати елемент в задану позицію, наприклад: int i; type c; list L; L
(с, i);
! =  перевірка на нерівність.
17. Клас  одновимірний масив. Додатково перевантажити наступні
операції:
+  додавання масивів;
[]  доступ за індексом;
+  додати елемент в кінець масиву.
18. Клас  одновимірний масив. Додатково перевантажити наступні
операції:
–  віднімання масивів;
[]  доступ заіндексом;
–  відняти з масиву елемент.
Лабораторна робота № 6
КЛАСИ ПОТОКІВ

Мета. Навчитися програмувати введення і виведення даних, на основі


потокових класів стандартної бібліотеки С++.
Основний зміст роботи. Створення призначеного для користувача типу
даних, створення і збереження об'єктів цього типу в файлі, читання їх з
файлу, видалення з файлу, коригування в файлі, створення призначених для
користувача маніпуляторів.
Основні теоретичні відомості.
Поняття потоку. Потокові класи представляють об'єктно-орієнтований
варіант функцій ANSI-C. Потік даних між джерелом і приймачем при цьому
володіє наступними властивостями.
 джерело або приймач даних визначається об'єктом потокового класу.
 потоки використовуються для введення-виведення високого рівня.
 загальноприйняті стандартні С-функції введення/виведення
розроблені як методи потокових класів, щоб полегшити перехід від С-
функцій до С++ класів.
 потокові класи діляться на три групи (шаблонів класів):
o basic_istream, basic_ostream – загальні потокові класи, які

можуть бути пов'язані з будь-яким буферним об'єктом;


o basic_ifstream, basic_iostream – потокові класи для
зчитування і запису файлів;
o basic_istringstream, basic_ostringstream – потокові класи для

об'єктів-рядків.
 кожен потоковий клас підтримує буферний об'єкт, який надає пам'ять
для переданих даних, а також найважливіші функції введення/виведення
низького рівня для їх обробки.
 базовим шаблоном класів basic_ios (для потокових класів) і
basic_streambuf (для буферних класів) передаються по два параметра
шаблону:
o перший параметр (charT) визначає символьний тип;

o другий параметр (traits) – об'єкт типу ios_traits (шаблон

класу), в якому задані тип і функції, специфічні для


використовуваного символьного типу;
o для типів char і wchar_t утворені відповідні об'єкти типу
ios_traits і потокові класи.
Приклад шаблону потокового класу:
template <class charT, class traits = ios_traits <charT >> class
basic_istream: virtual public basic_ios <charT, traits>;
Потокові класи в С++.Бібліотека потокових класів С++ побудована на
основі двох базових класів: ios і streambuf.
Клас streambuf забезпечує організацію та взаємозв'язок буферів
введення-виведення, що розміщуються в пам'яті, з фізичними пристроями
введення-виведення (рис. 3). Методи і дані класу streambuf програміст явно
зазвичай не використовує. Цей клас потрібен для інших класів бібліотеки
введення-виведення. Він доступний і програмісту для створення нових класів
на основі вже існуючих.

Рис. 3. Схема ієрархії буферного потоку

Клас ios містить засоби для форматованого введення-виведення і


перевірки помилок (рис. 4):
istream – клас вхідних потоків;
ostream – клас вихідних потоків;
iostream – клас введення-виведення;
istrstream – клас вхідних строкових потоків;
ifstream – клас вхідних файлових потоків і т.д.

Рис. 4. Схема ієрархії форматованого потоку

Потокові класи, їх методи і дані стають доступними в програмі, якщо в


неї включений потрібний заголовки файл:
iostream.h – для ios, ostream, istream.
strstream.h – для strstream, istrstream, ostrstream
fstream.h – для fstream, ifstream,ofstream
Базові потоки введення-виведення. Для введення з потоку
використовуються об'єкти класу istream, для виведення в потік – об'єкти
класу ostream.
Таблиця 2
Методи класів
Клас Метод Призначення
istream istream & get (char * buffer, метод витягує символи з istream і копіює їх в буфер.
int size, char delimiter = '\ n') Операція припиняється при досягненні кінця файлу
або копіювання size символів, або при виявленні
вказаного роздільника. Сам роздільник не буде
копіюватися і залишається в streambuf. Послідовність
прочитаних символів завжди завершується нульовим
символом.
istream & read (char * buffer, не підтримує роздільників, і зчитані в буфер
int size) символи не завершуються нульовим символом.
istream & getline (char * роздільник витягується з потоку, але в буфер не
buffer, int size, char delimiter заноситься. Це основний метод для отримання рядків
= '\ n') з потоку. Зчитаний рядок завершується нульовим
символом.
istream & get (streambuf & s, копіює дані з istream в streambuf до тих пір, поки не
char delimiter = '\ n') виявить кінець файлу або символ-роздільник, який
не витягається з istream. В s нульовий символ не
записується.
istream get (char & С) читає символ з istream в С. В разі помилки С
приймає значення 0XFF.
int get () витягує з istream черговий символ. При виявленні
кінця файлу повертає EOF.
int peek () повертає черговий символ з istream, не витягуючи
його з istream.
int gcount () повертає кількість символів, зчитаних під час
останньої операції неформатованого введення.
istream & putback (С) якщо в області get об'єкта streambuf є вільний
простір, то туди розміщується символ С.
istream & ignore (int count = витягує символ з istream, поки не відбудеться
1, int target = EOF) наступне:
 опція витягне count символів;
 не буде виявлений символ target;
 не буде досягнуто кінця файлу.
ostream ostream & put (char C) розміщує в ostream символ С.
ostream & write (const char * записує в ostream вміст буферу. Символи копіюються
buffer, int size) до тих пір, поки не виникне помилка, чи не буде
скопійовано size символів. Буфер записується без
форматування. Обробка нульових символів нічим не
відрізняється від обробки інших. Здійснює передачу
необроблених даних (бінарних або текстових) в
ostream.
ostream & flush () скидає буфер streambuf
istream istream & seekg (long p) встановлює вказівник потоку get (не плутати з
(прями методом) зі зміщенням р від початку потоку
й
доступ
)
istream & seekg (long p, вказується початкова точка зміщення. Enum seek_dir
seek_dir poin t) {beg, curr, end}. Позитивне значення р переміщує
вказівник get вперед (до кінця потоку), від'ємне
значення р – назад (до початку потоку)
long tellg () повертає поточне положення вказівника get
ostream ostream & seekp (long p) переміщує вказівник put в streambuf на позицію р від
(прями початку буферу streambuf
й
доступ
)
ostream & seekp (long p, вказує точка відліку
seek_dir point)
long tellp () повертає поточне положення вказівника put

Крім цих методів в класі istream перевантажена операція >>, а в класі


ostream <<. Операції << і >> мають два операнди Лівим операндом є об'єкт
класу istream (ostream), а правим – це, тип який заданий в мові.
Для того щоб використовувати операції << і >> для всіх стандартних
типів даних використовується відповідне число перевантажених функцій
operator<< і operator>>. При виконанні операцій введення-виведення в
залежності від типу правого операнда викликається та чи інша
перевантажена функція operator.
Підтримуються наступні типи даних: цілі, дійсні, рядки (char *). Для
виведення – void * (всі вказівники, відмінні від char *, автоматично
переводяться до void *). Перевантаження операції >> і << не змінює їх
пріоритету.
Функції operator << і operator >> повертають посилання на той
потоковий об'єкт, який вказаний зліва від знака операції. Таким чином,
можливе формування "ланцюжка" операцій.
cout << a << b << c ;
cin >> i >> j >> k;
При введенні-виведенні можна виконувати форматування даних.
Щоб використовувати операції >> і << з даними типів, визначених
користувачем, необхідно розширити дію цих операцій, ввівши нові операції-
функції. Першим параметром операції-функції має бути посилання на об'єкт
потокового типу, другим – посилання або об'єкт для користувача типу.
У файлі iostream.h визначені наступні об'єкти, пов'язані зі
стандартними потоками введення-виведення:
cin – об'єкт класу istream, пов'язаний зі стандартним вхідним потоком
буферу;
cout – об'єкт класу ostream , пов'язаний зі стандартним вихідним
потоком буферу;
cerr – не буферний вихідний потік для повідомлення про помилки;
clog – буферний вихідний потік для повідомлення про помилки.

Форматування. Безпосереднє застосування операцій введення << і


виведення >> до стандартних потоків cout , cin , cerr , clog для даних базових
типів приводить до використання форматів «по замовчуванню» зовнішнього
подання значень, що пересилаються.
Формати подання виведеної інформації та правила відображення даних
при введенні можуть бути змінені програмістом за допомогою прапорів
форматування. Ці прапори успадковані всіма потоками з базового класу ios.
Прапори форматування реалізовані у вигляді окремих фіксованих бітів і
зберігаються в protected полі класу long x_flags. Для доступу до них є
відповідні public функції.
Крім прапорів форматування використовуються наступні protected поля
класу ios :
int x_width – мінімальна ширина поля виведення;
int x_precision – точність подання дійсних чисел (кількість цифр
дробової частини) при виведенні;
int x_fill – символ-заповнювач при виведенні, пробіл – за
замовчуванням. Для отримання (установки) значень цих полів
використовуються методи:
int width ();
int width (int);
int precision ();
int precision (int);
char fill ();
char fill (char);
Маніпулятори. Незважаючи на гнучкість і великі можливості
управління форматами за допомогою методів класу ios, їх застосування
достатньо громіздко. Ще простішу зміну параметрів і прапорів форматування
забезпечують маніпулятори.
Маніпуляторами називаються методи, які модифікувати роботу потоку.
Особливість маніпуляторів полягає в тому, що їх можна використовувати в
якості правого операнда операції >> або <<. Як лівий операнд, зазвичай,
використовується потік (посилання на потік), і саме на цей потік впливає
маніпулятор.
Для забезпечення роботи з маніпуляторами в класах istream і ostream є
такі перевантажені оператори:
istream & operator >> (istream & (*_f) (istream &));
ostream & operator << (ostream & (*_f) (ostream &));
При використанні маніпуляторів слід включити заголовки <iomanip.h>,
в якому визначені вбудовані маніпулятори.
Визначення власних маніпуляторів. Порядок створення призначеного
для користувача маніпулятора з параметрами, наприклад для виведення,
наступний:
1. Визначити клас (my_manip) з полями: параметри маніпулятора,
вказівник на функцію типу ostream & (* f) (ostream &, <параметри
маніпулятора>);
2. Визначити конструктор цього класу (my_manip) з ініціалізацією
полів.
3. Визначити, в цьому класі дружню функцію – operator <<. Ця функція
в якості правого аргументу приймає об'єкт класу my_manip, лівий аргумент
(операнд) потік ostream і повертає потік ostream як результат виконання
функції * f. Наприклад,
typedef far ostream & (far * PTF) (ostream &, int, int, char);
class my_manip {
int w;
int n;
char fill;
PTF f;
public:
// конструктор
my_manip (PTF F, int W, int N, char FILL): f(F), w(W), n(N), fill(FILL) {};
friend ostream & operator << (ostream &, my_manip);
};
ostream & operator << (ostream & out, my_manip my)
{return my.f (out, my.w, my.n, my.fill);}

4. Визначити функцію типу * f (fmanip), приймаючу потік і параметри


маніпулятора і повертає потік. Ця функція власне і виконує форматування.
Наприклад,
ostream & fmanip (ostream & s, int w, int n, char fill)
{s.width(w);
s.flags(ios::fixed);
s.precision(n);
s.fill(fill);
return s;}
5. Визначити власне маніпулятор (wp) як функцію, що приймає
параметри маніпулятора і повертає об'єкт my_manip, поле f якого містить
вказівник на функцію fmanip.
Наприклад,
my_manip wp (int w, int n, char fill)
{return my_manip (fmanip, w, n, fill);}

Щоб створити власні маніпуляторів з параметрами можна використати


макроси, які містяться в файлі <iomanip.h>:
OMANIP (int)
IMANIP (int)
IOMANIP (int)
Cтан потоку. Кожен потік має пов'язаний з ним стан. Стану потоку
описуються в класі ios у вигляді перерахування enum.
public :

enum io_state {
goodbit, // немає помилки 0х00
eofbit, // кінець файлу 0х01
failbit, // остання операція не виконалася 0Х02
badbit, // спроба використання неприпустимої операції 0Х04
hardfail // фатальна помилка0Х08

};
Прапори, що визначають результат останньої операції з об'єктом ios,
містяться в змінній state. Отримати значення цієї змінної можна за
допомогою функції int rdstate ().
Крім того, перевірка стану потоку здійснюється за допомогою таких
функцій:
int bad (); 1, якщо badbit або hardfail
int eof (); 1, якщо eofbit
int fail (); 1, якщо failbit, badbit або hard fail
int good (); 1, якщо goodbit

Якщо операція >> використовується для нових типів даних, то при її


перевантаженні необхідно передбачити відповідні перевірки.
Файлове введення-виведення. Потоки для роботи з файлами
створюються як об'єкти класів:
 ofstream – запис в файл;
 ifstream – читання з файлу;
 fstream – читання/запис.
Для створення потоків є такі конструктори:
– fstream () – створює потік, не приєднуючи його до жодного файлу;
– fstream (const char* name, int mode, int p = filebuf::openprot) – створює
потік, приєднує його до файлу з назвою name, попередньо відкривши файл,
встановлює для нього режим mode і рівень захисту p. Якщо файл не існує, то
він створюється. Для mode = ios::out, якщо файл існує, то його розмір буде
усічений до нуля.
Прапори режиму визначені в класі ios і мають наступні:
Режим Значення
in для читання
out для запису
ate індекс потоку розміщений в кінець файлу, читання більше не
допустимо, дані записуються в кінець файлу
app потік відкритий для додавання даних в кінець. Незалежно від
seekp() дані будуть записуватися в кінець
trunc усічення існуючого потоку до нуля
nocreate команда відкриття потоку буде завершена невдало, якщо файл
не існує
noreplace команда відкриття потоку буде завершена невдало, якщо файл
існує
binary потік відкривається для двійкового обміну
Якщо при створенні потоку він не приєднаний до файлу, то приєднати
існуючий потік до файлу можна функцією:
void open (const char * name, int mode, int p = filebuf :: openprot);
функція
void fstreambase :: close ();
скидає буфер потоку, від'єднує потік від файлу і закриває файл. Цю
процедуру потрібно явно викликати при зміні режиму роботи з потоком.
Автоматично вона викликається тільки при завершенні програми.
Таким чином, створити потік і зв'язати його з файлом можна трьома
способами:
1. Створюється об'єкт filebuf;
Об'єкт filebuf зв'язується з пристроєм (файлом);
Створюється потік і зв'язується з filebuf :
filebuf fbuf;
fbuf.open ("назва", ios::in);
istream stream (& fbuf);
2. Створюється об'єкт fstream (ifstream, ofstream);
Відкривається файл, який зв'язується через filebuf з потоком:
fstream stream;
stream.open ("назва", ios::in);
3. Створюється об'єкт fstream, одночасно відкривається файл, який
зв'язується з потоком:
fstream stream ("назва", ios::in);
Порядок виконання роботи.
1. Визначити тип даних (клас). Визначити і реалізувати в ньому
конструктори, деструктор, оператори присвоєння, введення і виведення для
стандартних потоків.
2. Написати програму № 1 для створення об'єктів класу (введення
інформації з клавіатури з використанням перевантаженої операції >>) і
збереження їх в потоці (файлі). Передбачити в програмі виведення
повідомлення про кількість збережених об'єктів і про довжину отриманого
файлу в байтах.
3. Виконати тестування програми.
4. Реалізувати для виведення в потік свій маніпулятор з параметрами.
5. Написати програму № 2 для читання об'єктів з потоку, збереження їх
в масиві і перегляду масиву. Для перегляду об'єктів використовувати
перевантажену для cout операцію << і свій маніпулятор. Передбачити в
програмі виведення повідомлення про кількість прочитаних об'єктів і байтів.
6. Виконати програму для читання з файлу збережених програмою №1
об'єктів і їх перегляду.
7. Написати програму № 3 для додавання об'єктів в потік.
8. Виконати програму, додавши в потік кілька об'єктів і переглянути
отриманий файл.
9. Написати програму № 4 для видалення об'єктів з файлу.
10. Виконати програму, видаливши з потоку кілька об'єктів і
переглянути отриманий файл.
11. Написати програму № 5 для коригування (тобто зміни) записів в
файлі.
12. Виконати програму і переглянути отриманий файл.
Методичні вказівки.
1. Проект повинен містити 5 модулів (по числу програм).
2. Як тип даних взяти клас з лабораторної роботи № 1. Поля класу типу
char * замінити на char [ціле].
3. В сукупності програмою повинні використовувати всі класи потоків:
istream , ostream , fstream , ifstream , ofstream.
4. Також в програмах слід показати всі три способи створення потоку і
відкриття файлу.
5. Необхідно продемонструвати читання з файлу і запис в файл як за
допомогою функцій read/write, так і за допомогою перевантажених операцій
>> і <<.
6. Маніпулятор користувача створюється не менш ніж з двома
параметрами.
7. Визначення класу зберегти h-файл.
8. Визначення методів класу зберегти cpp-файл.
9. Реалізацію маніпулятора зберегти h-файл. Як параметри
маніпулятора можна використовувати:
а) ширину поля виведення;
б) точність виведення дійсних чисел;
в) символ-заповнювач;
г) спосіб вирівнювання (до лівої чи правої межі) і т.д.
10. У потік записати не менше 5 об'єктів.
11. Після запису об'єктів в файл і перед читанням їх з файлу визначити
кількість записаних об'єктів і вивести цю інформацію.
Визначити кількість записаних в файл об'єктів можна таким чином:
а) стати на кінець файлу  функції seekp (), seekg ();
б) визначити розмір файлу в байтах  функції tellp (), tellg ();
в) визначити кількість записаних об'єктів – розмір файлу поділити на
розмір об'єкта.
12. Необхідно тестувати помилки при роботі з файлом. Для цього слід
використовувати перевантажені операції: operator !(), operator void*() і
функції bad (), good ().
13. Оскільки в файлі може зберігатися будь-яка, заздалегідь не відома,
кількість об'єктів, для їх збереження в програмі № 2 при читанні з файлу
використовувати динамічний масив.
14. Слід визначити функцію find(), яка приймає значення ключового
поля об'єкта і повертає зсув цього об'єкта від початку файлу. Викликати цю
функцію перед видаленням/зміною об'єкта у файлі.
15. Для зміни і видалення об'єкта написати функції del () і repl () , яким
передається посилання на потік, зміщення від початку файлу запису
(результат виклику функції find), нове значення змінної запису.
Зміст звіту.
1. Титульний лист.
2. Постановка завдання.
3. Визначення класу.
4. Реалізація маніпулятора.
5. Реалізація функцій find (), del () і repl ().
6. Пояснення до програм. Для кожної програми вказується, які потокові
класи в ній використовуються, як створюються об'єкти потокових класів, як
відкриваються файли, яким чином виконується введення і виведення даних.
Лабораторна робота № 7
СТАНДАРТНА БІБЛІОТЕКА ШАБЛОНІВ

Мета. Освоїти технологію узагальненого програмування з


використанням бібліотеки стандартних шаблонів (STL) мови C++.
Основний зміст роботи. Написати три програми з використанням STL.
Перша і друга програми повинні демонструвати роботу з контейнерами STL,
третя  використання алгоритмів STL.
Основні теоретичні відомості.
Стандартна бібліотека шаблонів (STL). STL забезпечує загальні,
стандартні класи і функції, які реалізують найбільш популярні і широко
використовувані алгоритми і структури даних.
STL будується на основі шаблонів класів, і тому що входять до неї
алгоритми і структури, застосовні майже до всіх типів даних.
Склад STL. Ядро бібліотеки утворюють три елементи:
 контейнери;
 алгоритми;
 ітератори.
Контейнери (containers) – це об'єкти, призначені для зберігання інших
елементів. Наприклад, вектор, лінійний список, множина.
Асоціативні контейнери (associative containers) дозволяють за
допомогою ключів отримати швидкий доступ до даних.
У кожному класі-контейнері визначено набір функцій для роботи з
ними. Наприклад, список містить функції для вставки, видалення і злиття
елементів.
Алгоритми (algorithms) виконують операції над вмістом контейнера.
Існують алгоритми для ініціалізації, сортування, пошуку, заміни вмісту
контейнерів. Багато алгоритмів призначені для роботи з послідовністю
(sequence), яка представляє собою лінійний список елементів всередині
контейнера.
Ітератори (iterators) – це об'єкти, які по відношенню до контейнера
грають роль вказівників. Вони дозволяють отримати доступ до вмісту
контейнера приблизно так само, як вказівники використовуються для доступу
до елементів масиву.
З ітераторами можна працювати так само, як з вказівниками. До них
можна застосувати операції *, ++, --. Типом ітератору оголошується тип
iterator , який визначений в різних контейнерах.
Існує п'ять типів ітераторів:
1. Ітератори введення (input_iterator) підтримують операції рівності,
розіменування і інкременту: ==,! =, *і ,++i , i++, *i++
Спеціальним випадком ітератора введення є istream_iterator.
2. Ітератори виведення (output_iterator) підтримують операції
розіменування, допустимі тільки з лівого боку присвоєння і інкременту: ++і,
i++, *i = t , *i++ = t.
Спеціальним випадком ітератора виведення є ostream_iterator.
3. Односпрямовані ітератори (forward_iterator) підтримують всі операції
ітераторів введення/виведення і, крім того, дозволяють без обмеження
застосовувати присвоювання: ==,!=, =, *і,++i, i++, *i++.
4. Двонаправлені ітератори (biderectional_iterator) мають всі властивості
forward-ітераторів, а також мають додаткову операцію декременту (--i, i-, *i-),
що дозволяє їм проходити контейнер в обох напрямках.
5. Ітератори довільного доступу (random_access_iterator) мають усі
властивості biderectional-ітераторів, а також підтримують операції порівняння
та адресної арифметики, тобто безпосередній доступ по індексу:
i += n, i + n, i-= n, i-n, i1-i2, i[n], i1 <i2, i1 <= i2, i1> i2, i1> = i2.
У STL також підтримуються зворотні ітератори (reverse iterators).
Зворотними ітераторами можуть бути або двонаправлені ітератори, або
ітератори довільного доступу, але проходять послідовність в зворотному
напрямку.
Крім контейнерів, алгоритмів та ітераторів в STL підтримується ще
кілька стандартних компонентів. Головними серед них є розподільники
пам'яті , предикати і функції порівняння.
У кожного контейнера є певний для нього розподільник пам'яті
(allocator), який управляє процесом виділення пам'яті для контейнера.
За замовчуванням розподільником пам'яті є об'єкт класу allocator.
Можна визначити власний розподільник.
У деяких алгоритмах і контейнерах використовується функція
особливого типу, звана предикатом. Предикат може бути унарним і бінарним.
Значення, що повертається: true або false. Точні умови отримання того чи
іншого значення визначаються програмістом. Тип унарних предикатів
UnPred, бінарних – BinPred. Тип аргументів відповідає типу об'єктів, що
зберігаються в контейнері.
Визначено спеціальний тип бінарного предиката для порівняння двох
елементів. Він називається функцією порівняння (comparison function).
Функція повертає істину, якщо перший елемент менше другого. Типом
функції є тип Comp.
Особливу роль в STL грають об'єкти-функтори. Об'єкти-функтори  це
екземпляри класу, в якому визначена операція «круглі дужки» (). У ряді
випадків зручно замінити функцію на об'єкт-функтор. Коли об'єкт-функтор
використовується в якості функції, то для його виклику використовується
operator ().
Приклад.
class less {
public:
bool operator () (int x, int y) {
return x <y;
}
};
Класи-контейнери. У STL визначені два типи контейнерів:
послідовності і асоціативні.
Ключова ідея для стандартних контейнерів полягає в тому, що коли це є
розумним, вони повинні бути логічно взаємозамінними. Користувач може
вибирати між ними, ґрунтуючись на міркуваннях ефективності і потреби в
спеціалізованих операціях. Наприклад, якщо часто потрібен пошук по ключу,
можна скористатися map (асоціативним масивом). З іншого боку, якщо
переважають операції, характерні для списків, можна скористатися
контейнера list. Якщо додавання і видалення елементів часто проводиться в
кінці контейнера, слід подумати про використання черги – queue,
двосторонньої черги – deque, стека – stack. За замовчуванням користувач
повинен використовувати vector; він реалізований для самого широкого
діапазону задач.
Ідея звернення до різних видів контейнерів і, в загальному випадку, з
усіма видами джерел інформації – уніфікованим способом веде до поняття
узагальненого програмування. Для підтримки цієї ідеї STL містить множину
узагальнених алгоритмів. Такі алгоритми позбавляють програміста від
необхідності знати подробиці окремих контейнерів.
У STL визначено такі класи-контейнери:
Клас-контейнер Структура даних, що реалізовано Файл
bitset множина бітів < bitset.h >
vector динамічний масив < vector.h >
list лінійний список < list.h >
deque двостороння черга < deque.h >
stack стек < stack.h >
queue черга < queue.h >
priority_queue черга з пріоритетом < queue.h >
map асоціативний список для < map.h >
зберігання пар ключ/значення, де з
кожним ключем пов'язане одне
значення
multimap з кожним ключем пов'язане два або < map.h >
більше значень
set множина < set.h >
multiset множина, в якому кожен елемент < set. h >
не обов'язково унікальний

Огляд алгоритмів.
Типи даних в STL:
value_type тип елемента
allocator_type тип розподільника пам'яті
size_type тип індексів, лічильника елементів і т.д.
iterator поводиться як value_type *
reverse_iterator переглядає контейнер в зворотному порядку
reference поводиться як value_type &
key_type тип ключа (для асоціативних контейнерів)
key_compare тип критерію порівняння (для асоціативних
контейнерів)
mapped_type тип відображеного значення
Ітератори:

begin () вказує на перший елемент


end () вказує на елемент, наступний за останнім
rbegin () вказує на перший елемент в зворотному порядку
rend () вказує на елемент, за останнім зворотному
порядку
Доступ до елементів:
front () посилання на перший елемент
back () посилання на останній елемент
operator [] (i) доступ за індексом без перевірки
at (i) доступ за індексом з перевіркою
Включення елементів:
insert (p, x) додавання х перед елементом, на який вказує р
insert (p, n, x) додавання n копій х перед р
insert (p, first, last) додавання елементів з [first; last] перед р
push_back (x) додавання х в кінець
push_front (x) додавання нового першого елемента (тільки
для списків і черг з двома кінцями)
Видалення елементів:
pop_back () видаляє попередньо елемента
pop_front () видалення першого елемента (тільки
для списків і черг з двома кінцями)
erase (p) видалення елемента в позиції р
erase (first , last) видалення елементів з [first; last]
clear () видалення всіх елементів
Інші операції:
size () кількість елементів
empty () контейнер порожній?
capacity () пам'ять, виділена під вектор (тільки для векторів)
reserve (n) виділяє пам'ять для контейнера під n елементів
resize (n) змінює розмір контейнера (тільки для векторів,
списків і черг з двома кінцями)
swap (x) обмін місцями двох контейнерів
== , != , <, > операції порівняння
Операції присвоювання:
operator = (x) контейнеру присвоюються елементи контейнера х
assign (n, x) присвоювання контейнеру n копій елементів х (не
для асоціативних контейнерів)
assign (first, last) присвоювання елементів з діапазону [ first : last ]
Асоціативні операції:
operator [] (k) доступ до елементу з ключем k
find (k) знаходить елемент з ключем k
lower_bound (k) знаходить перший елемент з ключем k
upper_bound (k) знаходить перший елемент з ключем, великим k
equal_range (k) знаходить lower_bound (нижню межу) і
upper_bound (верхню межу) елементів з ключем k

Контейнер vector – вектор. Вектор – vector в STL визначено як


динамічний масив з доступом до елементів по індексу.
template <class T, class Allocator = allocator <T >> class std::vector {...};
де T – тип призначених для зберігання даних, Allocator – задає розподільник
пам'яті, який за замовчуванням є стандартним.
У класі vector визначені наступні конструктори:
explicit vector (const Allocator & a = Allocator ()) – конструктор
порожнього вектора;
explicit vector (size_type число, const T & значення = T (), const Allocator
& a = Allocator ()) – конструктора вектора кількість елементів – це число , а
кожен елемент дорівнює значення. Параметр значення може бути значенням
по замовчуванню.
vector (const vector <T, Allocator> & об'єкт) – конструктор копіювання;
template <class InIter> vector (InIter початок, InIter кінець, const
Allocator & a = Allocator ()) – конструктор вектора, що містить діапазон
елементів, який заданий ітераторами початок і кінець.
Приклади.
vector <int> a;
vector <double> x (5);
vector <char> c (5, '*');
vector <int> b (a); // b = a

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


визначений конструктор за замовчуванням. Крім того, для об'єкта повинні
бути визначені оператори <, > і ==, !=.
Для класу вектор визначені наступні оператори порівняння: ==, <, <=,!
=,>, > =. Крім цього, для класу vector визначається оператор індексу []. Нові
елементи можуть включатися за допомогою функцій: insert (), push_back (),
resize (), assign (). Існуючі елементи можуть віддалятися за допомогою
функцій: erase (), pop_back (), resize (), clear (). Доступ до окремих елементів
здійснюється за допомогою ітераторів: begin (), end (), rbegin (), rend ().
Маніпулювання контейнером, сортування, пошук в ньому та ін.
можливо за допомогою глобальних функцій файлу <algorithm.h >.
Приклади.
#include <iostream.h>
#include <vector.h>
using namespace std;
void main () {
vector <int> v;
int i;
for (i = 0; i <10; i++)
v.push_back (i);
cout << "size =" << v.size () << "\ n";
for (i = 0; i <10; i++)
cout << v [i] << "";
cout << endl;
for (i = 0; i <10; i++)
v [i] = v [i] + v [i];
for (i = 0; i <v.size (); i++)
cout << v [i] << "";
cout << endl;
}
// Доступ до вектору через итератор.
#include <iostream.h>
#include <vector.h>
using namespace std;
void main () {
vector <int> v;
int i;
for (i = 0; i <10; i++)
v.push_back (i);
cout << "size =" << v.size () << "\ n";
vector <int> :: iterator p = v.begin ();
while (p! = v.end ()){
Cout << * p << " ";
p++;
}
}
// Вставка і видалення елементів.
#include <iostream.h>
#include <vector.h>
using namespace std;
void main () {
vector <int> v (5,1);
int i;
// Вивід
for (i = 0; i <5; i++)
cout << v [i] << "";
cout << endl;
vector <int> :: iterator p = v.begin ();
p += 2;
// вставити 10 елементів зі значенням 9
v.insert (p , 10,9);
// вивід
p = v.begin ();
while (p! = v.end ()){
cout << * p << " "; p++;}
// Видалити вставлені елементи
p = v.begin ();
p += 2;
v.erase (p, p + 10);
// вивід
p = v.begin ();
while (p! = v.end ()) {
cout << * p << " ";
p++;
}
}
// Вектор містить об'єкти класу.
#include <iostream.h>
#include <vector.h>
#include "student.h"
using namespace std;
void main () {
vector <STUDENT> v (3);
int i;
v[0] = STUDENT ("Іваненко", 45.9);
v[1] = STUDENT ("Петренко", 30.4);
v[0] = STUDENT ("Сидоренко", 55.6);
// Вивід
for (i = 0; i <3; i++)
cout << v [i] << "";
cout << endl;
}
Асоціативні контейнери (масиви). Асоціативний масив містить пари
значень. Знаючи одне значення, зване ключем (key), ми можемо отримати
доступ до іншого, яке називають відображеним значенням (mapped value).
Асоціативний масив можна уявити як масив, для якого індекс не
обов'язково повинен мати цілочисельний тип:
V & operator [] (const K &) – повертає посилання на значення V ,
відповідне K.
Асоціативні контейнери – це узагальнення поняття асоціативного
масиву.
Асоціативний контейнер map  це послідовність пар (ключ, значення),
яка забезпечує швидке отримання значення по ключу. Контейнер map
підтримує двонаправлені ітератори.
Асоціативний контейнер map вимагає, щоб для типів ключа була
визначена операція "<". Він зберігає свої елементи відсортованими по ключу
так, що перебір відбувається по порядку.
Специфікація шаблону для класу map:
template <class Key, classT, class Comp = less <Key>,
class Allocator = allocator <pair> > class std :: map
У класі map визначені наступні конструктори:
explicit map (const Comp & c = Comp (), const Allocator & a = Allocator ()
– конструктор порожнього асоціативного контейнера;
map (const map <Key, T, Comp, Allocator> & ob) – конструктор копії;
template <class InIter> map (InIter first, InIter last, const Comp & c =
Comp (), const Allocator & a = Allocator ()) – конструктор асоціативного
контейнера, що містить діапазон елементів.
Визначено операція присвоювання: map & operator = (const map &).
Визначено такі операції: ==, <, <=,! =,>, > =. У map зберігаються пари
ключ/значення в вигляді об'єктів типу pair.
Створювати пари ключ/значення можна не тільки за допомогою
конструкторів класу pair, але і за допомогою функції make_pair, яка створює
об'єкти типу pair , використовуючи типи даних в якості параметрів.
Множина set. Множину set можна розглядати як асоціативний масив, в
якому значення не грають ролі, тому що ми оперуємо тільки ключами.
template <classT, class cmp = less <T>, class Allocator = allocator <T >>
class std :: set {...};
Множина, як і асоціативний масив, вимагає, щоб для типу T існувала
операція "менше" (<). Вона зберігає свої елементи відсортованими, так що
перебір відбувається по порядку.
Алгоритми. Кожен алгоритм виражається шаблоном функції або
набором шаблонів функцій. Таким чином, алгоритм може працювати з дуже
різними контейнерами, що містять значення різноманітних типів. Алгоритми,
які повертають ітератор, як правило, для повідомлення про невдачу
використовують кінець вхідної послідовності. Алгоритми не виконують
перевірки діапазону на їх вході і виході. Коли алгоритм повертає ітератор, це
буде ітератор того ж типу, що й був на вході. Алгоритми в STL реалізують
більшість поширених універсальних операцій з контейнерами, такі як
перегляд, сортування, пошук, вставка і видалення елементів.
Алгоритми визначені в заголовки < algorithm.h >.
Нижче наведено назви деяких найбільш часто використовуваних
функцій-алгоритмів STL.
Немодифікуючі операції.
for_earch () виконує операції для кожного елемента
послідовності
find () знаходить перше входження значення в
послідовність
find_if () знаходить першу відповідність предикату в
послідовності
count () підраховує кількість входжень значення в
послідовність
count_if () підраховує кількість виконань предиката в
послідовності
search () знаходить перше входження послідовності як
підпослідовності
search_n () знаходить n-е входження значення в послідовність
Модифікуючі операції.
copy () копіює послідовність, починаючи з першого
елементу
swap () міняє місцями два елементи
replace () замінює елементи із зазначеним значенням
replace_if () замінює елементи при виконанні предикату
replace_copy () копіює послідовність, замінюючи елементи із
зазначеним значенням
replace_copy_if () копіює послідовність, замінюючи елементи при
виконанні предикату
fill () замінює всі елементи даними значенням
remove () видаляє елементи з даними значенням
remove_if () видаляє елементи при виконанні предикату
remove_copy () копіює послідовність, видаляючи елементи із
зазначеним значенням
remove_copy_if () копіює послідовність, видаляючи елементи при
виконанні предикату
reverse () змінює порядок проходження елементів на
зворотний
random_shuffle () перемішує елементи згідно випадковому
рівномірному розподілу ("тасує" послідовність)
transform () виконує задану операцію над кожним елементом
послідовності
unique () видаляє рівні сусідні елементи
unique_copy () копіює послідовність, видаляючи рівні сусідні
елементи
Сортування.
sort () сортує послідовність з хорошою середньої
ефективністю
partial_sort () сортує частину послідовності
stable_sort () сортує послідовність, зберігаючи порядок
слідування рівних елементів
lower_bound () знаходить перше входження значення в
відсортованій послідовності
upper_bound () знаходить перший елемент, більший ніж задане
значення
binary_search () визначає, чи є даний елемент у відсортованій
послідовності
merge () зливає дві відсортовані послідовності
Робота з множинами.
includes () перевірка на належність множин
Мінімуми і максимуми.
min () менше з двох
max () більше з двох
min_element () найменше значення в послідовності
max_element () найбільше значення в послідовності
Перестановки.
next_permutation () наступна перестановка в лексикографічному
порядку
pred_permutation () попередня перестановка в лексикографічному
порядку

Порядок виконання роботи.


Написати і відлагодити три програми. Перша програма демонструє
використання контейнерних класів для зберігання вбудованих типів даних.
Друга програма демонструє використання контейнерних класів для
зберігання користувацьких типів даних.
Третя програма демонструє використання алгоритмів STL.
У програмі № 1 виконати наступне:
1. Створити об'єкт-контейнер відповідно до варіанта завдання і
заповнити його даними, тип яких визначається варіантом завдання.
2. Переглянути контейнер.
3. Змінити контейнер, видаливши з нього одні елементи і замінивши на
інші.
4. Переглянути контейнер, використовуючи для доступу до його
елементів ітератори.
5. Створити другий контейнер цього ж класу і заповнити його даними
того ж типу, що й перший контейнер.
6. Змінити перший контейнер, видаливши з нього n елементів після
заданого і додавши потім в нього всі елементи з другого контейнера.
7. Переглянути перший і другий контейнери.
У програмі № 2 виконати те ж саме, але для даних типу клас.
У програмі № 3 виконати наступне:
1. Створити контейнер, що містить об'єкти класу. Тип контейнера
вибирається відповідно до варіанта завдання.
2. Відсортувати його по спаданню елементів.
3. Переглянути контейнер.
4. Використовуючи відповідний алгоритм, знайти в контейнері елемент,
що задовольняє заданій умові.
5. Перемістити елементи, що задовольняють заданій умові в інший
(попередньо порожній) контейнер. Тип другого контейнера визначається
варіантом завдання.
6. Переглянути другий контейнер.
7. Відсортувати перший і другий контейнери по зростанню елементів.
8. Переглянути їх.
9. Отримати третій контейнер шляхом злиття перших двох.
10. Переглянути третій контейнер.
11.Порахувати, скільки елементів, що задовольняють заданій умові,
містить третій контейнер.
12. Визначити, чи є в третьому контейнері елемент, що задовольняють
задану умову.
Методичні вказівки.
1. Проект повинен містити 3 модулі (по числу програм).
2. Як користувацький типу даних використовувати клас лабораторної
роботи № 7.
3. При створенні контейнерів в програмі № 2 об'єкти завантажувати з
потоку, для чого використовувати програми запису і читання потоку з
лабораторного роботи № 7.
4. Для вставки і видалення елементів контейнера в програмі № 2
використовувати відповідні операції, визначені в класі контейнера.
5. Для створення другого контейнера в програмі № 3 можна
використати або алгоритм remove_copy_if, або визначити свій алгоритм
copy_if , якого немає в STL.
6. Для пошуку елемента в колекції можна використовувати алгоритм
find_if, або for_each, або binary_search, якщо контейнер відсортований.
7. Для порівняння елементів при сортуванні по зростанню
використовується операція <, яка повинна бути перевантажена в
призначеному для користувача класі. Для сортування по спаданню слід
написати функцію comp і використовувати другу версію алгоритму sort.
8. Умови пошуку і заміни елементів вибираються самостійно і для них
пишеться функція-предикат.
9. Для введення-виведення об'єктів користувацького класу слід
перевантажити операції ">>" і "<<".
10. Деякі алгоритми можуть не підтримувати використовувані у вашій
програмі контейнери. Наприклад, алгоритм sort не підтримує контейнери, які
не мають ітераторів довільного доступу. В цьому випадку слід написати свій
алгоритм. Наприклад, для стека алгоритм сортування може виконуватися в
такий спосіб: переписати стек в вектор, впорядкувати вектор, переписати
вектор в стек.
11. При переміщенні елементів асоціативного контейнера в
неасоціативні переміщаються тільки дані (ключі не переміщаються). І
навпаки, при переміщенні елементів неасоціативного контейнера в
асоціативний повинен бути сформований ключ.
Зміст звіту.
1. Титульний лист.
2. Постановка задач.
3. Визначення користувацького класу.
4. Визначення використовуваних в програмах компонентних функцій
для роботи з контейнером, включаючи конструктори.
5. Пояснення цих функцій.
6. Пояснення використовуваних в програмах алгоритмів STL.
7. Визначення і пояснення, що використовуються предикатів і функцій
порівняння.
Варіанти завдань.
вбудований
№ п/п перший контейнер другий контейнер
тип даних
1 vector list int
2 list deque long
3 deque stack float
4 stack queue double
5 queue vector char
6 vector stack string
7 map list long
8 multimap deque float
9 set stack int
10 multiset queue char
11 vector map double
12 list set int
13 deque multiset long
14 stack vector float
15 queue map int
16 priority_queue stack char
17 map queue char
18 multimap list int
19 set map char
20 multiset vector int

СПИСОК ЛІТЕРАТУРИ

Основна
1. Буч Г. Об'єктно-орієнтований аналіз та проектування з прикладами

додатків на С ++ / Пер. І.Романовскій, Ф.Андреев. - 2-е вид. - М., СПб .:


«Біном», «Невський діалект», 2008. - 344 с.
2. Бьярне Страуструп. Программирование: принципы и практика
использования C++, исправленное издание – М.: «Вильямс», 2011. – С. 1248.
3. Шілдт Г. C++: базовый курс, 3-е издание = C++ from the Ground Up
Third Edition. — М.: «Вильямс», 2012.
4. Лафоре Р. Объектно-ориентированное программирование в С++. 4-е,
ПИТЕР, 2004. – 928 с.

Додаткова
1. Аммераль Л. STL для програмістів на С++.  М., ДМК, 1999
2. Паппас К., Мюррей У. Visual C++ 6: Керівництво розробника. Київ:
BHV, 2000.
3. Шілдт Г. Полный справочник по C++, 4-е издание (C++: The
Complete Reference, 4th Edition.) — М.: «Вильямс», 2011. — 800 с..
4. Стивен Прата. Язык программирования C++ (C++11). Лекции и
упражнения. – 2012. – 1248 с.

You might also like