
- •Міністерство надзвичайних ситуацій України Львівський державний університет безпеки життєдіяльності Юрій грицюк, Тарас рак
- •Навчальний посібник
- •Потреба використання об'єктно-орієнтованого програмування
- •Поняття про об’єктно-орієнтований підхід до розроблення складних програм
- •Основні компоненти об’єктно-орієнтованої мови програмування
- •Поняття про універсальну мову моделювання
- •Базові поняття класу
- •Код програми 2.1. Демонстрація механізму оголошення класу та його застосування
- •Поняття про конструктори і деструктори
- •Особливості реалізації механізму доступу до членів класу
- •Класи і структури - споріднені типи
- •Об'єднання та класи - споріднені типи
- •Поняття про вбудовані функції
- •Inline int myClass::Put() return c;
- •Особливості організації масивів об'єктів
- •Особливості використання покажчиків на об'єкти
- •Поняття про функції-"друзі" класу
- •Код програми 3.1. Демонстрація механізму використання "дружньої" функції для доступу до закритих членів класу
- •Код програми 3.2. Демонстрація механізму використання "дружньої" функції для перевірки статусу кожного об'єкта
- •Void Run(); //Таймер відліку часу
- •Void timerClass::Run()
- •Int mainO
- •Особливості механізму динамічної ініціалізації конструктора
- •Int s; public:
- •Void Run(); //Таймер відліку часу
- •Void timerClass::Run()
- •Int mainO
- •Особливості механізму присвоєння об'єктів
- •Int а, ь; public:
- •Int mainO
- •Особливості механізму передачі об'єктів функціям
- •Void Fun(myClassobj)
- •Int mainO
- •Конструктори, деструктори і передача об'єктів
- •Void Get(myClass obj)
- •Int mainO
- •Потенційні проблеми, які виникають при передачі об'єктів
- •Int *р; public:
- •Void Get(myClass &obj) // Передача об'єкта за посиланням
- •Int mainO
- •Особливості механізму повернення об'єктів функціями
- •Void Set(char*s) {strcpy(str, s);}
- •Void Show() {cout «"Рядок:" « str« endl;}
- •Int mainO
- •Int mainO
- •Механізми створення та використання конструктора копії
- •Використання конструктора копії для ініціалізації одного об'єкта іншим
- •Int mainO
- •Механізм використання конструктора копії для передачі об'єкта функції
- •Int mainO
- •Механізм використання конструктора копії при поверненні функцією об'єкта
- •Int mainO
- •3.7.4. Конструктори копії та їх альтернативи
- •Поняття про ключове слово this
- •Void Fun() {...};
- •Int mainO
- •Механізми перевизначення операторів з використанням функцій-членів класу
- •Int х, у, z; //Тривимірні координати
- •Int mainO
- •Intх,у,z; //Тривимірні координати
- •Void Show(char*s);
- •Int mainO
- •Int х, у, z; //Тривимірні координати
- •Int mainO
- •Особливості реалізації механізму перевизначення операторів
- •Механізми иеревизначення операторів з використанням функцій-не членів класу
- •Використання функцій-"друзів" класу для перевизначення бінарних операторів
- •Void Show(char*s);
- •Int mainO
- •Int mainO
- •Використання функцій-"друзів" класу для перевизначення унарних операторів
- •Int mainO
- •Особливості реалізації оператора присвоєння
- •Int mainO
- •Int mainO
- •Механізми перевизначення оператора індексації елементів масиву "[]"
- •Int mainO
- •Int aMas[size]; public:
- •Int mainO
- •Int aMas[size]; public:
- •Int mainO
- •Механізми перевизначення оператора виклику функцій "()"
- •Int mainO
- •Механізми перевизначення рядкових операторів
- •Конкатенація та присвоєння класу рядків з рядками класу
- •Void Show(char*s) {cout« s « string « endl;}
- •Конкатенація та присвоєння класу рядків з рядками, що закінчуються нульовим символом
- •Void Show(char*s) {cout« s « string « endl;}
- •Void Show(char*s) {cout« s « string « endl;}
- •Int mainO
- •Поняття про успадкування в класах
- •Int kolesa; // Кількість коліс int pasagyr; // Кількість пасажирів public:
- •Int mistkist; // Вантажомісткість у м куб. Public:
- •Int kolesa; // Кількість коліс int pasagyr; // Кількість пасажирів public:
- •Int mainO
- •Використання специфікатора доступу protected для надання членам класу статусу захищеності
- •Int mainO
- •Int mainO
- •Int d; // Захищений public:
- •Int mainO
- •Protected-членом класу derived, що робить його недоступним за його межами. */
- •Void showX() {cout« х « endl;}
- •Void showY() {cout« у « endl;}
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Успадкування віртуальних функцій
- •Void Show() {cout« Суг("Другий похідний клас.") « endl;}
- •Virtual void Show() {cout« Суг("Базовий клас.") « endl;}
- •Void Show() {cout« Суг("Перший похідний клас. ")« endl;}
- •Int mainO
- •Virtual void ShowO {cout« Суг("Базовий клас.") « endl;}
- •Void Show() {cout« Суг("Перший похідний клас. ")« endl;}
- •Int mainO
- •Void Show()
- •Void Show()
- •Int mainO
- •Void Show()
- •Void Show()
- •Int mainO
- •Void swapAb(aType &а, аТуре &b)
- •Int mainO
- •Приклад створення узагальненого класу для організації безпечного масиву
- •Int mainO
- •Використання в узагальнених класах аргументів, що не є узагальненими типами
- •Використання в шаблонних класах аргументів за замовчуванням
- •Int mainO
- •Механізм реалізації безпосередньо заданої спеціалізації класів
- •Int mainO
- •Основні особливості оброблення виняткових ситуацій
- •Системні засоби оброблення винятків
- •Xtest(1);
- •Xtest(2);
- •Перехоплення винятків класового типу
- •Використання декількох catch-наетанов
- •Варіанти оброблення винятків
- •Перехоплення всіх винятків
- •Накладання обмежень на тип винятків, які генеруються функціями
- •Int mainO
- •Xhandler(o); // Спробуйте також передати функції XhandlerO аргументи 1 і 2.
- •Void Xhandler(int test) throw 0
- •Повторне генерування винятку
- •Int mainO
- •Int mainO
- •Механізми перевизначення операторів new і delete
- •Void *р;
- •Void *p;
- •Int mainO
- •Класи потоків
- •Особливості механізмів перевизначення операторів введення-виведення даних
- •Створення перевюначених операторів виведення даних
- •Int mainO
- •Використання функцій-"друзів" класу для перевюначення операторів виведення даних
- •Int х, у, z; //Тривимірні координати (тепер це private-члени) public:
- •Int mainO
- •Створення перевюначених операторів введення даних
- •Istream &operator»(istream &stream, kooClass &obj)
- •Cout«"Введіть координати X, у і z:
- •Stream » obj.X » obj.Y » obj.Z;
- •Istream &operator»(istream &stream, objectType &obj)
- •// Код операторної функції введення даних
- •Class kooClass {// Оголошення класового типу int х, у, z; // Тривимірні координати
- •Istream &operator»(istream &stream, kooClass &obj)
- •Int mainO
- •Void unsetf(fmtflags flags),
- •Void showflags(ios::fmtflags f); // Відображення поточного стану опцій
- •Int mainO
- •Ios::fmtflags f; // Оголошення параметру для поточного стану опцій
- •Int mainO
- •Створення власних маніиуляторних функцій
- •Організація файлового введення-виведення даних
- •Відкриття та закриття файлу
- •Зчитування та запис текстових файлів
- •Ifstream in(argv[1], ios::in | ios::binary); if(!in){
- •In.CloseO; getchO; return 0;
- •Int mainO
- •Зчитування та записування у файл блоків даних
- •Int mainO
- •Ifstream inftest", ios::in | ios::binary); if(!in){
- •Використання функції eof() для виявлення кінця файлу
- •If(!in.Eof()) cout« ch;
- •In.CloseO; getchO; return 0;
- •Int main(int arge, char *argv[])
- •Ifstream f1(argv[1], ios::in | ios::binary); if(!f1) {
- •Ifstream f2(argv[2], ios::in | ios::binary);
- •Int main(int arge, char *argv[])
- •Ifstream in(argv[1], ios::in | ios::binary); if(!in){
- •In.Seekg(atoi(argv[2]), ios::beg); while(in.Get(ch)) cout« ch; getchO; return 0;
- •Int х, у, z; // Тривимірні координати; вони тепер закриті public:
- •Int mainO
- •Virtual void Fun() {}; // Робимо клас Base поліморфним
- •Int mainO
- •Virtual void FunO {cout«"у класі Base"« endl;}
- •Int mainO
- •Virtual void FunO {}
- •Void derivedOnlyO {cout«"Це об'єкт класу Derived"« endl;}
- •Int mainO
- •Void Fun(const int *р)
- •Int mainO
- •Namespace ns { int d;
- •Застосування настанови using
- •Int Comp(const void *а, const void *b);
- •Int mainO
- •Int Comp(const void *a, const void *b)
- •Int mainO
- •Int Fun10 const; // const-функція-член
- •Int PutO const; return c; // Все гаразд
- •Int mainO
- •0Bj.Set(1900); cout « Obj.PutO;
- •Void setJ(int х) const // Наступна функція не відкомпілюється.
- •Int а; public:
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int myClass::*dp; // Покажчик на int-члена класу void (myClass::*fp)(int X); // Покажчик на функцію-члена
- •Int mainO
- •Механізми роботи з векторами
- •Int mainO
- •1 234 5 678 9 10 11 12 13141516 17 1819 Вміст подвоєно:
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Символи н
- •Символів представляють голосні звуки.
- •Int mainO
- •Int mainO
- •Int mainO
- •Void getaLine(string& inStr); // Отримання рядка тексту char getaCharO; //Отримання символу
- •Int aptNumber; // Номер кімнати мешканця
- •Void DisplayO; // Виведення переліку мешканців
- •Int aptNo; float rent[12]; public:
- •Void setRent(int, float); // Запис плати за місяць
- •Void insertRent(int, int, float); void DisplayO;
- •Int month, day; string category, payee; float amount; expense() {}
- •Int mainO
- •Void rentRecord::insertRent(int aptNo, int month, float amount)
- •SetPtrsRr.Insert(ptrRow); // Занести рядок вектор
- •If( setPtrsRr.Empty())
- •Else // Інакше
- •Int month, day; string category, payee; float amount;
- •«" 'Є' для запису витрат:";
- •Навчальний посібник
Void Show()
{
cout« Суг("Трикутник з висотою ")« x; cout« Cyr(" і основою") « у; cout« Cyr(" мас площу ") « x * 0.5 * у ; cout« Суг(" кв. од. ")« endl;
}
};
class rectangle: public figure { public:
Void Show()
{
cout« Суг("Прямокутник розмірами") « x «" x" « y; cout« Cyr(" мас площу ") «х* у; cout« Cyr(" кв. од. ")« endl;
}
};
Int mainO
//
Створення покажчика на об'єкт базового
типу // Створення об'єктів похідних
типів // Створення об'єкта похідного
типу
figure *p; triangle ObjT; rectangle ObjR;
р = &ObjT; // Присвоєння покажчику адреси об'єкта похідного класу
p->Set(10.3,5.5);
p->Show();
р = &ObjR; // Присвоєння покажчику адреси об'єкта похідного класу
p->Set(10.3,5.5); p->Show(); getchO; return 0;
}
Ось ж виглядають результати виконання цієї програми:
Трикутник з висотою 10.3 і основою 5.5 має площу 28.325 кв. од.
Прямокутник розмірами 10.3 х 5.5 має площу 56.65 кв. од.
У цьому коді програми зверніть увагу на те, що під час роботи з класами rectangle і triangle використано однаковий інтерфейс, хоча у кожному з них реалізовано власні методики обчислення площі відповідних об'єктів.
Як Ви думаєте, використовуючи оголошення базового класу figure, можна вивести похідний клас сігсіе для обчислення площі круга за заданим значенням радіуса? Відповідь: так. Для цього достатньо створити новий похідний тип, який би обчислював площу круга. Потужність віртуальних функцій опирається на той факт, що програміст може легко вивести новий тип, який розділятиме загальний інтерфейс з іншими "спорідненими" об'єктами. Ось, наприклад, як це можна зробити в нашому випадку: class сігсіе : public figure { public:
void ShowO {
cout « CyrfKpyr з радіусом ") « x;
cout « Cyr(" має площу ") « 3.14 * x * x « endl;
}
};
Перш ніж випробувати клас сігсіе в роботі, розглянемо уважно визначення функції ShowO- Зверніть увагу на те, що в ній використовується тільки одне значення змінної х, яка повинна містити радіус круга1. Проте, згідно з визначенням функції Set(), у базовому класі figure їй передається два значення, а не одне. Оскільки похідному класу сігсіе не потрібне друге значення, то що ми можемо зробити? Є два способи вирішити цю проблему. Перший (і одночасно найгірший) полягає у тому, що ми могли б, працюючи з об'єктом класу сігсіе, просто викликати функцію SetO, передаючи їй як другий параметр фіктивне значення. Основний недолік цього методу - відсутність чіткості у задаванні параметрів і потрібно пам'ятати про спеціальні винятки, які порушують дію механізму - один інтерфейс, багато методів.
Є вдаліший спосіб вирішення цього питання, який полягає у наданні параметру функції SetO значення, яке діє за замовчуванням. У цьому випадку під час виклику функції SetO Для круга потрібно задавати тільки радіус. Під час виклику ж функції SetO Для трикутника або прямокутника задають обидва значення. Нижче показано програму, у якій реалізовано цей підхід.
Код програми 6.6. Демонстрація механізму надання параметру віртуальної функції значення, що діє за замовчуванням
#include <vcl>
#include <conio>
#include <iostream> // Для потокового введення-виведення using namespace std; // Використання стандартного простору імен
class figure { protected:
double x, у; public:
void Set(double _x, double _y=0) {x = _x; у = _y;} virtual void Show()
{
cout« Суг("У цьому класі вираз для обчислення площі не визначено.") « endl;
}
};
class triangle: public figure { public:
void Show()
{
cout« Суг("Трикутник з висотою ")« x; cout« Cyr(" і основою") « у; cout« Cyr(" має площу ") « x * 0.5 * у ; cout« Суг(" кв. од. ")« endl;
}
};
class rectangle: public figure { public:
void Show()
{
cout« Суг("Прямокутник розмірами") « x «" x" « y; cout« Cyr(" має площу ") «х* у; cout« Cyr(" кв. од. ")« endl;
}
};
class circle: public figure { public:
void Show()
{
cout« Cyr("Kpyr з радіусом") « x;
cout« Cyr(" має площу ") «З .14159 * x * x; cout« Cyr(" кв. од.") << endl;
}
};
int mainO
//
Створення покажчика на об'єкт базового
типу // Створення об'єкта похідного
типу // Створення об'єкта похідного
типу // Створення об'єкта похідного
типу
//
Присвоєння покажчику адреси об'єкта
похідного класу
figure *p; triangle ObjT; rectangle ObjR; circle ObjC;
//
Присвоєння покажчику адреси об'єкта
похідного класу
p = &ObjR;
p->Set(10.3, 5.5); p->ShowO;
р = &ObjC; // Присвоєння покажчику адреси об'єкта похідного класу
p->Set(9.67);
p->ShowO;
getchO; return 0;
}
Внаслідок виконання ця програма відображає на екрані такі результати:
Трикутник з висотою 10.3 і основою 5.5 має площу 28.325 кв. од.
Прямокутник розмірами 10.3 х 5.5 має площу 56.65 кв. од.
Круг з радіусом 9.67 має площу 293.767 кв. од.
Вартоа нати'.аХоча віртуальні функції синтаксично прості для розуміння, їх справжні можливості неможливо продемонструвати на навчальних прикладах. Як правило, потужність поліморфізму виявляється у великих складних ієрархічних системах. У міру засвоєння мови програмування C++ Вам ще не раз буде надано можливість переконатися в їх користі.
Поняття про суто віртуальні функції та абстрактні класи
Як було зазначено вище, якщо віртуальна функція, яка не перевизначена у похідному класі, викликається об'єктом цього похідного класу, то використовується та її версія, яку було визначено в базовому класі. Але у багатьох випадках взагалі немає сенсу давати визначення віртуальної функції в базовому класі. Наприклад, в базовому класі figure (з попереднього прикладу) визначення функції Show() - це просто заглушка. Вона не обчислює і не відображає площу жодного з об'єктів. Як буде показано згодом, при створенні власних бібліотек класів, у тому, що віртуальна функція не має конкретного визначення у базовому класі, немає нічого незвичайного.
Існує два способи оброблення таких ситуацій. Перший (він показаний у наведеному вище прикладі коду програми) полягає у виведенні функцією застережного повідомлення. Можливо, такий підхід і буде корисний у певних ситуаціях, але здебільшого він просто неприйнятний. Наприклад, можна уявити собі віртуальні функції, без визначення яких у існуванні похідного класу взагалі немає ніякого сенсу. Розглянемо клас triangle. Він абсолютно зайвий, якщо у ньому не визначити функцію Show(). У цьому випадку варто створити метод, який би гарантував, що похідний клас дійсно містить усі необхідні функції. У мові програмування C++ для вирішення цього питання і передбачено суто віртуальні функції.
Суто віртуальна функція - це віртуальна функція, яку оголошено в базовому класі, але вона не має у ньому ніякого визначення. Тому будь-який похідний тип повинен визначити власну версію цієї функції, адже у нього просто немає ніякої можливості використовувати версію з базового класу (через її відсутність). Щоб оголосити суто віртуальну функцію, використовують такий загальний формат:
virtual тип ім'я_фуищї(перелік_парететрів) = 0;
У цьому записі під елементом тип маємо на увазі тип значення, що повертається функцією, а елемент ім'я_функції- використовуване у програмі її ім'я. Позначення = 0 є ознакою того, що функція тут оголошується як суто віртуальна. Наприклад, в наступній версії визначення базового класу figure функція Show() вже представлена як суто віртуальна: class figure { double х, у; public:
void Set(double _x, double _y=0) {x = _x; у = _y;} virtual void Show() = 0; // Суто віртуальна функція
};
Оголосивши функцію суто віртуальною, програміст створює умови, при яких похідний клас просто вимушений мати визначення власної її реалізації. Без цього компілятор видасть повідомлення про помилку. Наприклад, спробуйте скомпілювати цю модифіковану версію програми обчислення площ геометричних фігур, у якій з класу circle видалено визначення функції Show().
Код програми 6.7. Демонстрація не коректної програми, яка у класі circle немає перевизначення функції Show()
#include <vcl>
#include <conio>
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class figure { protected:
double x, у; public:
void Set(double _x, double _y) {x = _x; у = _y;} virtual void Show() = 0; // Суто віртуальна функція
};
class triangle: public figure { public: