- •1 Парадигми та мови програмування
- •1.1 Процедурне програмування
- •1.2 Об'єктне (модульне) програмування
- •1.3 Об'єктно-орієнтовне програмування
- •2 Програмне середовище
- •3 Базові поняття програмування
- •4 Процедурно-орієнтоване програмування
- •4.1 Функції
- •4.2 Вбудовані (inline) функції
- •4.3 Передача параметрів
- •4.4 Обчислення значення функцій
- •4.4. Обчислення значення функції — вихід із функції; особливості повернення та використання іменованого значення, іменованої константи
- •4.5 Рекурсія
- •4.5. Рекурсія
- •4.6 Довизначення (overloading) функцій
- •4.6. Довизначення (overloading) функцій
- •4.7 Узагальнені функції (function template)
- •4.8 Непряме використання функцій: указники на функції, їх тип, ініціалізація і присвоєння
- •4.9 Видимість
- •4.10 Тривалість життя об’єктів
- •5 Об'єктне програмування
- •5.1 Класи і об'єкти, члени класів
- •5.2 Інтерфейс класу, реалізація класу; визначення і оголошення класу
- •5.3 Створення і ініціалізація об'єктів, довизначення конструкторів, замовчуваний конструктор, копіювання, поверхневе і глибоке копіювання, ініціалізація і присвоєння, копіювальний конструктор
- •5.4 Cтатичні члени класів і статичні класні функції
- •5.5 Константні об'єкти, константні функції, змінювані члени константних об'єктів (mutable)
- •5.6 Поточний об'єкт this, указники на члени класу і класні функції, порівняння з указниками на (позакласні) функції, указники на статичні члени класу
4.7 Узагальнені функції (function template)
Узагальнені функції (function template)
Інший спосіб наділення однієї функції багатьма реалізаціями полягає у застосуванні узагальнених (generic) функцій. Для цього в С++ використовують так званий механізм шаблонів (template).
Розглянемо, наприклад, функцію swap, визначену нами для цілих чисел. При необхідності ми могли б довизначати її для інших типів, наприклад, дійсних чисел
void rswap( double &x, double &y)
{
double z = x; x=y; y=z;
}
При цьому легко помітити, що дії, які виконуються у функції, не залежать від типів її параметрів. На місці double міг би бути довільний тип, для якого визначена операція присвоєння. Ми могли б уявити собі таке параметричне визначення функції
void rswap( Type &x, Type &y)
{
Type z = x; x=y; y=z;
}
Тепер, правда, в програмі з’явився невизначений об’єкт Type. Залишилося сказати, що це довільний тип. Такого вигляду набуде повне визначення узагальненої функції
template <class Type>
void rswap( Type &x, Type &y)
{
Type z = x; x=y; y=z;
}
Узагальнені функції обробляються компілятором у дуже спеціальний спосіб. Компілятор не в стані побудувати об’єктний код, тому що різні типи даних можуть вимагати різних машинних команд для виконання однієї й тієї ж дії, хоч би й присвоєння. У випадку символу, це буде пересилка одного байту, для цілого — слова, для дійсного подвоєної точності — двох слів. Тому компілятор лише фіксує факт наявності шаблону, створюючи його конкретизації для кожного виклику. Так в контексті
char a, b;
rswap(a, b);
буде згенеровано об’єктний код для перестановки символів, а в контексті
float a, b;
rswap(a, b);
код для перестановки дійсних чисел. Або навіть
Point u, v;
rswap(u, v);
Шаблони можуть накладати певні обмеження на допустиму конкретизації. Якщо ми повернемося до прикладу з максимумом, то одержимо шаблон
template <class Type> Type max(Type x, Type y)
{
return (x>y? x: y);
}
Тут Type вже не будь-який тип, а лише такий, для якого визначено порівняння на нерівність. Компілятор не зможе згенерувати коду для спроби обчислити максимум двох точок
Point u, v, w;
w = max(u, v);
Інша проблема узагальнених функцій викликана складностями з перетворенням типів. Дійсно, один і той же типовий параметр, наприклад, T з попереднього прикладу не може набувати одного значення на місці першого аргументу і іншого на другому місці. Ось приклад, який показує проблеми і шляхи їх вирішення
template <class T> T mymax (T x, T y)
{
cout<<"template ";
return x>y? x: y;
};
Явна функція сильніша за узагальнену
long mymax(long x, long y)
{
cout<<"long ";
return x>y? x: y;
};
При необхідності змішувань типів доведеться користуватися додатковими специфікаціями
inline double mymax (int x, double y)
{
return mymax<double>((double)x, y);
};
Варіанти викликів
cout<<mymax(1,2)<<endl;
cout<<mymax('a','b')<<endl;
cout<<mymax(1l,3l)<<endl;
cout<<mymax(1l,4)<<endl; // error
cout<<mymax<long>(1,5)<<endl; // узагальнена
cout<<mymax<long>(1l,5)<<endl; // явна
cout<<mymax(1,3.14)<<endl;
cout<<mymax(1l,4.14)<<endl;
cout<<mymax('a',1)<<endl; // error