
- •7. Робота з шаблонними, функціями та класами
- •7.1. Поняття про узагальнені функції
- •7.1.1. Механізм реалізації шаблонної функції з одним узагальненим типом
- •7.1.2. Безпосередньо задане перевизначення узагальненої функції
- •7.1.3. Шаблонна функція з двома узагальненими типами
- •7.1.4. Механізм перевизначення специфікації шаблону функції
- •7.1.5. Використання стандартних параметрів у шаблонних функціях
- •7.1.6. Обмеження, які застосовуються при використанні узагальнених функцій
- •7.1.7. Приклад створення узагальненої функції abs()
- •7.2. Поняття про узагальнені класи
- •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. Застосування покажчиків на функції
Покажчик на функцію – це достатньо складний, але дуже потужний засіб C++-програмування. Незважаючи на те, що функція не є змінною, проте вона займає фізичну область пам'яті, певну адресу якої можна присвоїти покажчику. Адреса, що присвоюється покажчику, є вхідною точкою функції1. Якщо деякий покажчик посилається на функцію, то її (функцію) можна викликати за допомогою цього покажчика.
Покажчики на функції також дають змогу передавати функції як аргументи іншим функціям. Адресу функції можна отримати, використовуючи ім'я функції без круглих дужок і аргументів2. Якщо присвоїти адресу функції покажчику, то цю функцію можна викликати через покажчик. Щоб зрозуміти сказане, розглянемо наведену нижче програму. Вона містить дві функції – vLine() і hLine(), які малюють на екрані вертикальні та горизонтальні лінії заданої довжини.
Демонстрація механізму застосування покажчиків на функції
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
using namespace std; // Використання стандартного простору імен
void vLine(int i), hLine(int i);
int main()
{
void (*p)(int i);
p = vLine; // Покажчик на функцію vLine()
(*p)(4); // Виклик функції vLine()
p = hLine; // Покажчик на функцію hLine()
(*p)(5); // Виклик функції hLine()
getch(); return 0;
}
void hLine(int i)
{
for(; i; i--) cout << "-";
cout << endl;
}
void vLine(int i)
{
for(; i; i--) cout << "|" << endl;
}
Ось як виглядають результати виконання цієї програми:
|
|
|
|
–––––
Розглянемо цю програму у деталях. У першому рядку тіла функції main() визначається змінна р як покажчик на функцію, яка приймає один цілочисельний аргумент і не повертає ніякого значення. Це оголошення не визначає, про яку саме функцію йдеться. Воно тільки створює покажчик, який можна використовувати для адресації будь-якої функції цього типу. Необхідність круглих дужок, у які поміщено покажчик *р, випливає з С++-правил передування.
У наступному рядку покажчику р присвоюється адреса функції vLine(). Потім здійснюється виклик функції vLine() з аргументом 4. Після цього покажчику р присвоюється адреса функції hLine(), і за допомогою цього покажчика реалізується її виклик. У цьому коді програми під час виклику функцій за допомогою покажчика використовується такий формат:
(*р)(4);
Проте функцію, яка адресується покажчиком р, можна викликати з використанням дещо простішого синтаксису:
р(4);
Єдина причина, згідно з якою частіше використовується перший варіант виклику функції, полягає у тому, що всім, хто буде аналізувати Вашу програму, стане зрозуміло, що тут реалізовано виклик функції через покажчик р, а не виклик фун- кції з іменем р. У всьому іншому ці варіанти є еквівалентними.
11.2.2. Пошук адреси перевизначеної функції
Отримати адресу перевизначеної функції трохи складніше, ніж знайти адресу звичайної "одиночної" функції. Якщо ж існує декілька версій перевизначеної функції, то повинен існувати механізм, який би визначав, адресу якої саме її версії ми отримуємо. Під час отримання адреси перевизначеної функції саме спосіб оголошення покажчика визначає, адресу якої її версії буде отримано. По суті, оголошення покажчика у цьому випадку порівнюється з відповідними оголошеннями покажчиків перевизначених функцій. Функція, оголошення якої виявить збіг, і буде тією функцією, адресу якої ми отримали.
У наведеному нижче прикладі коду програми міститься дві версії функції space(). Перша версія виводить на екран count пропусків, а друга – count символів, переданих як аргумент ch. У функції main() оголошуються два покажчики на функції. Перший заданий як покажчик на функцію з одним цілочисельним параметром, а другий – як покажчик на функцію з двома параметрами.
Демонстрація механізму використання покажчиків на дві перевизначені функції для виведення на екран відповідно пропусків і символів
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
using namespace std; // Використання стандартного простору імен
// Введення на екран count пропусків.
void space(int count)
{
for(; count; count–-) cout << " ";
}
// Введення на екран count символів, переданих в ch.
void space(int count, char ch)
{
for(; count; count-–) cout << ch;
}
int main()
{
// Створення покажчика на void-функцію з одним int-параметром.
void (*fp1)(int);
/* Створення покажчика на void-функцію з одним int-параметром
і одним параметром типу char. */
void (*fp2)(int, char);
fp1 = space; // Отримуємо адресу функції space(int)
fp2 = space; // Отримуємо адресу функції space(int, char)
fp1(22); // Виводимо 22 пропуски (цей виклик є аналогічним
// виклику (*fp1)(22)).
cout << "|" << endl;
fp2(30, 'x'); // Виводимо 30 символів "x" (цей виклик є
// аналогічним до виклику (*fp2)(30, 'x').
cout << "|" << endl;
getch(); return 0;
}
Ось як виглядають результати виконання цієї програми:
|
хххххххххххххххххххххххххххххх|
Як зазначено у коментарях до цієї програми, компілятор здатний визначити, адресу якої перевизначеної функції він отримує, на основі того, як оголошено покажчики fp1 і fр2.
Отже, коли адреса перевизначеної функції присвоюється покажчику на функцію, то саме це оголошення покажчика слугує основою для визначення того, адресу якої функції було присвоєно. При цьому оголошення покажчика на функцію повинно відповідати одній (і тільки одній) з перевизначених функцій. Інакше у програму вноситься неоднозначність, яка викличе помилку компілювання.