
- •Міністерство надзвичайних ситуацій України Львівський державний університет безпеки життєдіяльності Юрій грицюк, Тарас рак
- •Навчальний посібник
- •Потреба використання об'єктно-орієнтованого програмування
- •Поняття про об’єктно-орієнтований підхід до розроблення складних програм
- •Основні компоненти об’єктно-орієнтованої мови програмування
- •Поняття про універсальну мову моделювання
- •Базові поняття класу
- •Код програми 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;
- •«" 'Є' для запису витрат:";
- •Навчальний посібник
Namespace ns { int d;
}
У цьому записі простір імен NS розділено на дві частини. Проте вміст кожної частини належить до одного і того ж простору імен NS.
Будь-який простір імен повинен бути оголошений поза всіма іншими областями видимості. Це означає, що не можна оголошувати простори імен, які локалізовані, наприклад, у межах функції. При цьому один простір імен може бути вкладено в інший.
Застосування настанови using
Якщо програма містить багато посилань на члени певного простору імен, то неважко уявити, що потреба вказувати ім'я цього простору імен при кожному зверненні до них, дуже скоро набридне Вам. Цю проблему дає змогу вирішити настанова using, яка застосовується у таких двох форматах: using namespace ім'я, using пате::чпен;
Настанова using робить заданий простір імен "видимим", тобто діючим.
У першій формі елемент ім'я задає назву простору імен, до якого Ви зможете отримати доступ. Всі члени, визначені усередині заданого простору імен, потрапляють в "поле видимості", тобто стають частиною поточного простору імен і їх можна потім використовувати без кваліфікації (уточнення простору імен). У другий формі робиться "видимим" тільки вказаний член простору імен. Наприклад, вважаючи, що простір імен CounterNameSpace визначено (як це показано вище), то наступні настанови using і присвоєння будуть цілком законними:
using CounterNameSpace::lowerbound; // Видимим став тільки член lowerbound. lowerbound = 10; // Все гаразд, оскільки член lowerbound знаходиться в області видимості.
using namespace CounterNameSpace; // Всі члени видимі, upperbound = 100; // Все гаразд, оскільки всі члени видимі.
Використання настанови using продемонстровано у наведеному нижче коді програми (яка є новим варіантом лічильника з попереднього розділу).
Код програми 11.2. Демонстрація механізму використання настанови using для виконання розрахунку у зворотному порядку
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
using namespace std; // Використання стандартного простору імен
namespace CounterNameSpace {
int upperbound; int lowerbound;
class counter { int count; public:
counter(int n) {
if(n <= upperbound) count = n; else count = upperbound;
}
void Reset(int n) {
if(n <= upperbound) count = n;
}
int RunO {
if(count > lowerbound) return count--; else return lowerbound;
}
};
}
int mainO
{
// Використовується тільки член upperbound з // простору імен CounterNameSpace. using CounterNameSpace::upperbound;
//Тепер для встановлення значення змінній upperbound // не потрібно вказувати простір імен, upperbound = 100;
// Але під час звернення до змінної lowerbound і до інших // об'єктів, як і раніше, необхідно вказувати простір імен. CounterNameSpace::lowerbound = 0;
CounterNameSpace::counter ObjA (10); int c;
cout«"Розрахунок у зворотному порядку для об'єкта ObjA"« endl; do {
с = ObjA.RunO; cout« c «"";
} while(c > CounterNameSpace::lowerbound); cout« endl;
//Тепер використовуємо весь простір імен CounterNameSpace. using namespace CounterNameSpace;
counter 0bjß(20);
cout«"Розрахунок у зворотному порядку для об'єкта ObjB"« endl; do {
с = ObjB.RunO; cout« c «"";
}while(c> lowerbound); cout« endl;
ObjB.Reset(IOO); lowerbound = 80;
cout«"Розрахунок у зворотному порядку для об'єкта ObjB"« endl; do {
с = ObjB.RunO;
cout« с «"
} while(c > lowerbound); cout« endl;
getchO; return 0;
}
Ця програма ілюструє ще один важливий момент: якщо певний простір імен стає "видимим", то це означає, що він просто додає свої імена до імен інших, вже діючих просторів. Тому до кінця цієї програми до глобального простору імен додалися і std, і CounterNameSpace.
Неіменовані простори імен
Існує неіменований простір імен спеціального типу, який дає змогу створювати ідентифікатори, унікальні для цього файлу. Загальний формат його оголошення має такий вигляд:
namespace {
// Оголошення
}
Неіменовані простори імен дають змогу встановлювати унікальні ідентифікатори, які відомі тільки в області видимості одного файлу. Іншими словами, члени файлу, який містить неіменований простір імен, можна використовувати безпосередньо, без уточнюючого префікса. Але поза файлом ці ідентифікатори є невідомими.
Неіменований простір імен обмежує ідентифікатори рамками файлу, у якому їх оголошено.
Як уже зазначалося вище у цьому навчальному посібнику, використання модифікатора типу static також дає змогу обмежити область видимості глобального простору імен файлом, у якому він оголошений. Наприклад, розглянемо такі два
файли, які є частиною однієї і тієї ж самої програми: |
|
Файл One |
Файл Two |
static int f; |
extern int f; |
void Fun10 { |
voidFun2(){ |
II CO CO о 7ч |
f = 10; //Помилка |
} |
} |
Оскільки змінну f визначено у файлі One, то її і можна використовувати у файлі One. У файлі Two змінну f визначено як зовнішню (extern-змінна), а це означає, що її ім'я і тип відомі, але саму змінну f насправді не визначено. Коли ці два файли будуть скомпоновані, спроба використовувати змінну f у файлі Two призведе до виникнення помилки, оскільки у ньому немає визначення для змінної f. Той факт, що f оголошена як static-змінна у файлі One, означає, що її область видимості обмежується цим файлом, і тому вона недоступна для файлу Two.
Незважаючи на те, що використання глобальних зіайс-оголошень все ще дозволено стандартом мови програмування С++, проте для локалізації ідентифікатора у межах одного файлу краще використовувати неіменований простір імен. Роз- глянемо такий приклад:
Файл One |
Файл Two |
namespace { intf; } static int f; void Fun10 { f = 99;// OK } |
extern int f; voidFun2(){ f = 10; //Помилка } |
Тут змінну f також обмежено рамками файлу One. Для нових програм рекомендується використовувати замість модифікатора static неіменований простір імен.
Зазвичай для більшості коротких програм і програм середнього розміру немає потреби у створенні просторів імен. Але, формуючи бібліотеки багаторазово використовуваних функцій або класів, є сенс помістити свій програмний код (якщо хочете забезпечити його максимальну переносність) у власний простір імен.
Застосування простору імен std
Стандарт мови програмування C++ визначає всю свою бібліотеку у власному просторі імен, який іменується std. Саме з цієї причини більшість програм у цьому навчальному посібнику містять таку настанову:
using namespace std; // Використання стандартного простору імен У процесі виконання цієї настанови простір імен std стає поточним, що відкриває прямий доступ до імен функцій і класів, визначених у цій бібліотеці, тобто під час звернення до них відпадає необхідність у використанні префікса std::.
Простір імен std використовується власною бібліотекою мови програмування C++.
Звичайно, при бажанні можна безпосередньо кваліфікувати кожне бібліотечне ім'я префіксом std::. Наприклад, наведений нижче код програми не привносить бібліотеку в глобальний простір імен.
Код програми 11.3. Демонстрація механізму використання безпосередньо заданої кваліфікації бібліотечних імен префіксом std::
#include <iostream> // Для потокового введення-виведення
int mainO
{
double n;
std::cout«"Введіть число:"; std::cin » n;
std::cout«"Ви ввели число";
std::cout« п; getchO; return 0;
}
У цьому коді програми імена cout і сіп безпосередньо доповнені іменами своїх просторів імен. Отже, щоб записати дані у стандартний вихідний потік, необхідно використовувати не просто ім'я потоку cout, а ім'я з префіксом std::cout, а щоб зчитати дані із стандартного вхідного потоку, потрібно застосувати "префік- сне" ім'я std::cin.
Якщо Ваша програма використовує стандартну бібліотеку тільки в обмежених межах, то, можливо, її і не варто вносити в глобальний простір імен. Але, якщо Ваша програма містить сотні посилань на бібліотечні імена, то набагато простіше зробити простір імен std поточним, ніж повністю кваліфікувати кожне ім'я окремо.
Якщо Ви використовуєте тільки декілька імен із стандартної бібліотеки, то, ймовірно, є сенс використовувати настанову using для кожного з них окремо. Перевага цього підходу полягає у тому, що ці імена можна, як і раніше, використовувати без префікса std::, не вносячи при цьому всю бібліотеку стандартних функцій у глобальний простір імен. Розглянемо такий приклад.
Код програми 11.4. Демонстрація механізму внесення в глобальний простір імен декількох імен
#include <iostream> // Для потокового введення-виведення
// Отримуємо доступ до імен потоків cout і сіп
using std::cout;
using std::cin;
int mainO
{
double n;
cout«"Введіть число:сіп » n;
cout«"Ви ввели число cout« п; getchO; return 0;
}
У цьому коді програми імена потоків сіп і cout можна використовувати безпосередньо, але інша частина простору імен std не внесена в область видимості.
Як уже зазначалося вище, початкова бібліотека мови програмування C++ була визначена в глобальному просторі імен. Якщо Вам доведеться модернізувати старі С++-програми, то програміст повинен або включити в них настанову using namespace std, або доповнити кожне звернення до члена бібліотеки префіксом std::. Це особливо важливо, якщо Вам доведеться замінювати старі заголовні Мі-файли сучасними заголовками.
Нео! хідноапам ятати! Старі заголовні *.1і-файли поміщають свій вміст у глобальний простір імен, а сучасні заголовки - у простір імен Std.
Застосування покажчиків на функції
Покажчик на функцію - це достатньо складний, але дуже потужний засіб С++-програмування. Незважаючи на те, що функція не є змінною, проте вона займає фізичну область пам'яті, певну адресу якої можна присвоїти покажчику. Адреса, що присвоюється покажчику, є вхідною точкою функції1. Якщо деякий покажчик посилається на функцію, то її (функцію) можна викликати за допомогою цього покажчика.
Покажчик на функцію посилається на вхідну точку цієї, функції.
Покажчики на функції також дають змогу передавати функції як аргументи іншим функціям. Адресу функції можна отримати, використовуючи ім'я функції без круглих дужок і аргументів2. Якщо присвоїти адресу функції покажчику, то цю функцію можна викликати через покажчик. Щоб зрозуміти сказане, розглянемо наведену нижче програму. Вона містить дві функції - vLineO і hLineO, які малюють на екрані вертикальні та горизонтальні лінії заданої довжини.
Код програми 11.5. Демонстрація механізму застосування покажчиків на функції
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
using namespace std; // Використання стандартного простору імен
void vLine(int і), hLine(int і);
int mainO
{
void (*p)(int і);
p = vLine; // Покажчик на функцію vLine()
(*p)(4); //Виклик функції vLineO
p = hLine; // Покажчик на функцію hLine()
(*p)(5); // Виклик функції hLine() getchO; return 0;
}
void hLine(int і)
{
for(; і; і--) cout« cout« endl;
}
void vLine(int і)
{
for(; і; і--) cout«"|" « endl;
}
Ось ж виглядають результати виконання цієї програми:
Розглянемо цю програму у деталях. У першому рядку тіла функції mainO визначається змінна р як покажчик на функцію, яка приймає один цілочисельний аргумент і не повертає ніякого значення. Це оголошення не визначає, про яку саме функцію йдеться. Воно тільки створює покажчик, який можна використовувати для адресації будь-якої функції цього типу. Необхідність круглих дужок, у які поміщено покажчик *р, випливає з С++-правил передування.
У наступному рядку покажчику р присвоюється адреса функції vLineO- Потім здійснюється виклик функції vLine() з аргументом 4. Після цього покажчику р присвоюється адреса функції hLine(), і за допомогою цього покажчика реалізується її виклик. У цьому коді програми під час виклику функцій за допомогою покажчика використовується такий формат:
(*р)(4);
Проте функцію, яка адресується покажчиком р, можна викликати з використанням дещо простішого синтаксису:
р(4);
Єдина причина, згідно з якою частіше використовується перший варіант виклику функції, полягає у тому, що всім, хто буде аналізувати Вашу програму, стане зрозуміло, що тут реалізовано виклик функції через покажчик р, а не виклик функції з іменем р. У всьому іншому ці варіанти є еквівалентними.
Передача покажчиком на функцію її адреси іншій функції
Незважаючи на те, що у наведеному вище прикладі покажчик на функцію використано тільки заради ілюстрації, часто таке його застосування має дуже важливе значення. Покажчик на функцію дає змогу передавати її адресу іншій функції. Як показовий приклад можна навести функцію qsortO із стандартної С++-бібліоте- ки. Функція qsortO - це функція швидкого сортування, що базується на алгоритмі Quicksort, який упорядковує вміст масиву. Її прототип має такий вигляд:
void qsort(void *start, size_t length size_t size,
int (*compare) (const void *, const void *));
Прототип функції qsortO "прописаний" у заголовку <cstdlib>, у якому також визначено тип size_t (як тип unsigned int). Щоб використовувати функцію qsortO, не~ обхідно передати їй покажчик на початок масиву об'єктів, який Ви хочете відсортувати (параметр start), довжину цього масиву (параметр length), розмір у байтах кожного елемента (параметр size) і покажчик на функцію порівняння елементів масиву (параметр * compare).
Функція порівняння, що використовується функцією qsort(), зіставляючи два елементи масиву, повинна повернути негативне значення, якщо її перший аргумент вказує на значення, яке є меншим від другого, нуль, якщо ці аргументи однакові, і позитивне значення, якщо перший аргумент вказує на значення, яке є більшим від другого.
Щоби зрозуміти, як можна використовувати функцію qsortO, розглянемо наведену нижче програму.
Код програми 11.6. Демонстрація механізму використання бібліотечної функції qsort() для сортування елементів текстового масиву
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
#include <cstdlifc» // Для використання бібліотечних функцій
#include <cstring> // Для роботи з рядковими типами даних
using namespace std; // Використання стандартного простору імен