- •7. Робота з шаблонними, функціями та класами
- •7.1. Поняття про узагальнені функції
- •7.1.1. Механізм реалізації шаблонної функції з одним узагальненим типом
- •7.1.2. Безпосередньо задане перевизначення узагальненої функції
- •7.1.3. Шаблонна функція з двома узагальненими типами
- •7.1.4. Механізм перевизначення специфікації шаблону функції
- •7.1.5. Використання стандартних параметрів у шаблонних функціях
- •7.2. Поняття про узагальнені класи
- •7.1.6. Обмеження, які застосовуються при використанні узагальнених функцій
- •7.1.7. Приклад створення узагальненої функції abs()
- •7.2.1. Створення класу з одним узагальненим типом даних
- •7.2.2. Створення класу з двома узагальненими типами даних
- •7.2.3. Приклад створення узагальненого класу для організації безпечного масиву
- •7.2.4. Використання в узагальнених класах аргументів, що не є узагальненими типами
- •11.2.1. Передача покажчиком на функцію її адреси іншій функції
- •9.2.3. Класи потоків
- •9.3. Особливості механізмів перевизначення операторів введення-виведення даних
- •9.3.1. Створення перевизначених операторів виведення даних
- •9.5. Організація файлового введення-виведення даних
- •9.5.1. Відкриття та закриття файлу
- •9.5.2. Зчитування та запис текстових файлів
- •9.5.3. Неформатне введення-виведення даних у двійковому режимі
- •9.5.4. Зчитування та записування у файл блоків даних
- •10. Динамічна ідентифікація типів і оператори приведення типу
- •10.1. Динамічна ідентифікація типів
- •10.1.1. Отримання типу об'єкта у процесі виконання програми
- •10.1.2. Приклад rtti-застосування
- •11. Поняття про простори імен та інші ефективні програмні засоби
- •11.1. Особливості організації простору імен
- •11.1.1. Поняття про простори імен
- •11.1.2. Застосування настанови using
- •11.1.3. Неіменовані простори імен
- •11.1.4. Застосування простору імен std
- •11.2. Застосування покажчиків на функції
- •11.2.2. Пошук адреси перевизначеної функції
- •11.3. Поняття про статичні члени-даних класу
- •11.5. Застосування до функцій-членів класу модифікаторів const і mutable
- •11.6. Використання explicit-конструкторів
- •12. Введення в стандартну бібліотеку шаблонів
- •12.1. Огляд стандартної бібліотеки шаблонів
- •12.2. Поняття про контейнерні класи
- •12.3. Механізми роботи з векторами
- •12.3.1. Доступ до елементів вектора за допомогою ітератора
- •12.3.2. Вставлення та видалення елементів з вектора
- •12.3.3. Збереження у векторі об'єктів класу
- •12.3.4. Доцільність використання ітераторів
- •7. Робота з шаблонними, функціями та класами 1
- •7.1. Поняття про узагальнені функції 1
- •10. Динамічна ідентифікація типів і оператори приведення типу 26
- •11. Поняття про простори імен та інші ефективні програмні засоби 31
- •12. Введення в стандартну бібліотеку шаблонів 44
11.2.1. Передача покажчиком на функцію її адреси іншій функції
Незважаючи на те, що у наведеному вище прикладі покажчик на функцію використано тільки заради ілюстрації, часто таке його застосування має дуже важливе значення. Покажчик на функцію дає змогу передавати її адресу іншій функції.
Як показовий приклад можна навести функцію qsort() із стандартної С++-бібліотеки. Функція qsort() – це функція швидкого сортування, що базується на алгоритмі Quicksort, який упорядковує вміст масиву. Її прототип має такий вигляд:
void qsort(void *start, size_t length size_t size,
int (*compare) (const void *, const void *));
Прототип функції qsort() "прописаний" у заголовку <cstdlib>, у якому також визначено тип size_t (як тип unsigned int). Щоб використовувати функцію qsort(), необхідно передати їй покажчик на початок масиву об'єктів, який Ви хочете відсортувати (параметр start), довжину цього масиву (параметр length), розмір у байтах кожного елемента (параметр size) і покажчик на функцію порівняння елементів масиву (параметр * compare).
Функція порівняння, що використовується функцією qsort(), зіставляючи два елементи масиву, повинна повернути негативне значення, якщо її перший аргумент вказує на значення, яке є меншим від другого, нуль, якщо ці аргументи однакові, і позитивне значення, якщо перший аргумент вказує на значення, яке є більшим від другого.
Щоби зрозуміти, як можна використовувати функцію qsort(), розглянемо наведену нижче програму.
Демонстрація механізму використання бібліотечної функції qsort() для сортування елементів текстового масиву
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
#include <cstdlib> // Для використання бібліотечних функцій
#include <cstring> // Для роботи з рядковими типами даних
using namespace std; // Використання стандартного простору імен
int Сomp(const void *a, const void *b);
int main()
{
char str[] = "Покажчики на функції дають гнучкість.";
qsort(str, strlen(str), 1, Сomp);
cout << "Відсортований рядок: " << str;
cout << endl;
getch(); return 0;
}
int Сomp(const void *a, const void *b)
{
return * (char *) a – * (char *) b;
}
Ось як виглядають результати виконання цієї програми.
Відсортований рядок: .Пааагджииііїккккннносттууфцччьью
Ця програма сортує рядок str у зростаючому порядку. Оскільки функції qsort() передається вся необхідна їй інформація, в т.ч. покажчик на функцію порівняння, то її можна використовувати для сортування даних будь-якого типу. Наприклад, наведений нижче код програми сортує масив цілих чисел. Для гарантії переносності під час визначення розміру цілочисельного значення в ній використовують оператор sizeof.
Демонстрація механізму використання бібліотечної функції qsort() для сортування елементів цілочисельного масиву
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
#include <cstdlib> // Для використання бібліотечних функцій
using namespace std; // Використання стандартного простору імен
int Сomp(const void *a, const void *b);
int main()
{
int num[] = {10, 4, 3, 6, 5, 7, 8};
qsort(num, 7, sizeof(int), Сomp);
for(int i=0; i<7; i++)
cout << num[i]<< " ";
cout << endl;
getch(); return 0;
}
int Сomp(const void *a, const void *b)
{
return * (char *) a – * (char *) b;
}
Не станемо заперечувати, що покажчики на функції є не простими для розуміння, але практика їх використання допоможе і "з ними знайти порозуміння". Відносно покажчиків на функції необхідно розглянути ще один аспект, що пов'язаний з перевизначеними функціями.
9. ОРГАНІЗАЦІЯ С++-СИСТЕМИ ВВЕДЕННЯ-ВИВЕДЕННЯ ПОТОКОВОЇ ІНФОРМАЦІЇ
З самого початку висвітлення матеріалу у цьому навчальному посібнику використовувалася С++-система введення-виведення потокової інформації, але не наводилось детальних пояснень з механізму її реалізації. Оскільки С++-система введення-виведення побудована на ієрархії класів, то її теорію і деталі реалізації неможливо було засвоїти, не розглянувши спочатку механізм реалізації класів, успадкування в класах і оброблення винятків. Якраз після цього і настає момент для детального вивчення С++-засобів введення-виведення потокової інформації.
У цьому розділі будуть розглядатися засоби як консольного, так і файлового введення-виведення потокової інформації. Необхідно зразу ж відзначити, що С++ - система введення-виведення – достатньо широка тема. Тому у поданому нижче матеріалі описано тільки найважливіші та часто вживані засоби організації С++ - системи введення-виведення. Зокрема, спочатку дізнаємося про те, що розуміють під потоками у мові програмування С++, про перевизначення операторів "<<" і ">>" для введення та виведення об'єктів, про форматування різних типів даних, а також про використання маніпуляторів введення-виведення. На завершення розділу переглянемо засоби файлового введення-виведення потокової інформації.
9.1. Порівняння C- та С++-систем введення-виведення
На сьогодні існують дві версії бібліотеки об'єктно-орієнтованого введення-виведення даних, причому обидві широко використовуються програмістами: стара [дод. А, 9], що базується на оригінальних специфікаціях мови C, і нова – визначається стандартом мови програмування C++. Стара бібліотека введення-виведення даних підтримується за рахунок заголовного файлу <iostream.h>, а нова – за допомогою заголовка <iostream>. Нова бібліотека введення-виведення даних загалом є оновленою і вдосконаленою версією старої. Основна відмінність між ними полягає в механізмі реалізації засобів введення-виведення потокової інформації, а не у тому, як їх потрібно використовувати.
З погляду програміста, є дві істотні відмінності між старою С- і новою С++ - бібліотеками введення-виведення. По-перше, нова бібліотека містить ряд додаткових засобів і визначає декілька нових типів даних. Тому нову бібліотеку С++ - системи введення-виведення можна вважати надбудовою над старою С-системою. Практично всі програми, що були написані раніше з використанням старої бібліотеки, успішно компілюються тепер за наявності нової бібліотеки, не вимагаючи внесення будь-яких значних змін у самій програмі. По-друге, стара бібліотека С-системи введення-виведення була визначена в глобальному просторі імен, а нова використовує простір імен std (пригадайте, що простір імен std використовується всіма бібліотеками стандарту мови програмування C++). Оскільки С-бібліотека введення-виведення даних вже дещо застаріла, то у цьому навчальному посібнику описується тільки нова її версія. Водночас велика частина наведеного нижче матеріалу повною мірою стосується і старої бібліотеки.
9.2. Поняття "потоків" у мові програмування C++
Принциповим для розуміння С++-системи введення-виведення є те, що вона базується на понятті "потоку". Потік (stream) – це загальний логічний інтерфейс з різними пристроями, з яких складається комп'ютер. Потік або синтезує інформацію, або споживає її, після чого зв'язується з будь-яким фізичним пристроєм за допомогою С++-системи введення-виведення. Характер поведінки всіх потоків є однаковим, незважаючи на різні фізичні пристрої, з якими вони пов'язуються. Оскільки різні потоки діють однаково, то практично до всіх типів пристроїв можна застосувати одні і ті ж самі функції та оператори введення-виведення. Наприклад, методи, що використовуються для виведення даних на екран, також можна вико- ристовувати для виведення їх на друкувальний пристрій або для запису у дисковий файл.
9.2.1. Файлові С++-потоки
У загальному випадку потік можна назвати логічним інтерфейсом з файлом. У мові програмування С++ до терміну "файл" належать дискові файли, екран монітора, клавіатура, порти, файли на магнітній стрічці й ін. Хоча файли між собою відрізняються за формою і можливостями представлення, проте робота з різними потоками інформації є однаковою. Перевага цього підходу (з погляду програміста) полягає у тому, що один пристрій комп'ютера може бути подібним до будь-якого іншого. Це означає, що потік забезпечує інтерфейс, який узгоджується зі всіма пристроями.
Потік зв'язується з файлом у процесі виконання операції відкриття файлу, а відокремлюється від нього за допомогою операції закриття.
Існує два типи потоків: текстовий і двійковий. Текстовий потік використовують для введення-виведення символів. При цьому можуть відбуватися деякі перетворення символів. Наприклад, під час виведення символ нового рядка може перетворюватися у послідовність символів: повернення каретки і переходу на новий рядок. Тому часто не буває взаємно-однозначної відповідності між тим, що посилається у потік, і тим, що насправді записується у файл. Двійковий потік можна використовувати з даними будь-якого типу, причому у цьому випадку ніякого перетворення символів не здійснюється, тобто між тим, що посилається у потік, і тим, що потім реально міститься у файлі, існує взаємно-однозначна відповідність.
Розглядаючи потоки, необхідно розуміти, що вкладається у поняття "поточної позиції". Наприклад, якщо довжина файлу дорівнює 100 байтів, і відомо, що вже прочитано його половину, то наступна операція зчитування почнеться на 50-му байті, який у цьому випадку і є поточною позицією.
Поточна операція - те місце у файлі, з якого буде виконуватися наступна операція доступу до його жаних.
Отже, у мові програмування C++ механізм введення-виведення потокової інформації функціонує з використанням логічного інтерфейсу, який називається потоком. Всі потоки мають аналогічні властивості, які дають змогу використовувати однакові функції введення-виведення, незалежно від того, з файлом якого типу існує зв'язок. Під файлом розуміють реальний фізичний пристрій, який містить дані. Якщо файли можуть відрізнятися між собою, то потоки – ні. Водночас, якщо деякі пристрої можуть не підтримувати всі операції введення-виведення даних (наприклад, операції з довільною вибіркою), то і пов'язані з ними потоки теж не підтримуватимуть ці операції.
9.2.2. Вбудовані С++-потоки
У мові програмування C++ міститься ряд вбудованих однобайтових (8-бітових) потоків (cin, cout, cerr і clog), які автоматично відкриваються, як тільки програма починає виконуватися. Як уже зазначалося вище, cin – це стандартний вхідний, а cout – стандартний вихідний потік. Потоки cerr і clog (вони призначені для виведення інформації про помилки) також пов'язані із стандартним виведенням даних. Різниця між ними полягає у тому, що потік clog є буферизованим, а потік cerr – ні. Це означає, що будь-які вихідні дані, послані у потік cerr, будуть негайно виведені на екран, а при використанні потоку clog дані спочатку записуються в буфер, а реальне їх виведення починається тільки тоді, коли буфер повністю заповнено. Зазвичай потоки cerr і clog використовують для виведення інформації на екран про стан відлагодження програми або її помилки.
У мові програмування C++ також передбачено двобайтові (16-бітові) символьні версії стандартних потоків, що іменуються wcin, wcout, wcerr і wclog. Вони призначені для підтримки таких мов як китайська, японська та інших східно-азійських, для представлення яких потрібні великі символьні набори. У цьому навчальному посібнику двобайтові стандартні потоки не розглядаються. За замовчуванням стандартні С++-потоки зв'язуються з монітором, але програмним способом їх можна перенаправити на інші зовнішні пристрої або дискові файли. Перенаправлення може також виконати сама операційна система.
