Лекція № 13
Тема: Перевантаження функцій. Параметри за замовчуванням.
План
1. Перевантаження функцій
2. Перевантаження конструкторів
3. Параметри за замовчуванням
Перевантаження функцій
Перевантаження функцій — це використання одного імені для декількох функцій. Секрет перевантаження полягає в тому, що кожне перевизначення функції повинне використовувати або інші типи параметрів, або іншу їх кількість. Тільки ці відмінності дозволяють компілятору визначати, яку функцію слід викликати в тому або іншому випадку. Наприклад, в наступній програмі функція myfunc () перегружена для різних типів параметрів.
#include <iostream>
using namespace std;
int myfunc(int i);
// Ці варіанти розрізняються типами параметрів
double myfunc(double і);
int main()
{
cout << myfunc(10)<< " "; // Виклик функції myfunc(int i)
cout << myfunc(5.4); //Виклик функції myfunc(double i)
return 0;
}
double myfunc(double i) { return і; }
int myfunc(int i) { return і; }
В наступній програмі перевантажені варіанти функції myfunc () використовують різну кількість параметрів.
#include <iostream>
using namespace std;
int myfunc(int i);
// Ці варіанти розрізняються кількістю параметрів
int myfunc(int і, int j);
int main()
{
cout << myfunc(10)<< " "; // Виклик функції myfunc(int i)
cout << myfunc(4, 5); // Виклик функції myfunc(int i, int j)
return 0;
int myfunc(int i)
{
return i;
}
int myfunc (int і, int j )
{
return x*j ;
}
Слід пям'ятати, що перевантажені функції повинні відрізнятися типами або кількістю параметрів. Тип значення, яке повертається, не дозволяє перенавантажувати функції. Наприклад, наступний варіант перевантаження функції myfunc () невірний.
int myfunc(int i); // Помилка: різних типів повертаємого
float myfunc(int і);
// значення недостатнь для перевантаження
Іноді оголошення двох функцій зовні відрізняються, але фактично співпадають. Приклад такого оголошення:
void f (int *p) ;
void f(int p[]); // Помилка, вирази *р і р[] еквівалентні
Треба пам'ятати, що компілятор не розрізняє виразу *р і р [ ]. Хоча зовні два прототипи функції f розрізняються, насправді вони повністю співпадають.
Перевантаження конструкторів
Конструктори можна перенавантажувати. Фактично їх-то і перенавантажують частіше за все. Для перевантаження конструктора існують три причини: гнучкість, можливість створення ініціалізованих (не ініціалізованих) об'єктів і конструкторів копіювання. Досить часто об'єкти класу можна створити декількома способами. Для кожного з цих способів можна визначити окремий варіант перевантаженого конструктора. Ці варіанти вичерпують всі можливості створити об'єкт — при спробі зробити це непередбаченим способом компілятор не знайде відповідного конструктора і видасть повідомлення про помилку.
Перевантажені конструктори набагато підвищують гнучкість класу. Вони дозволяють користувачу вибирати оптимальний спосіб створення об'єкту.Приклад програми, яка створює клас date для зберігання календарної дати. Туту конструктор перевантажений двічі.
#include <iostream>
#include <cstdio>
using namespace std;
class date
{
int day, month, year;
public:
date(char *d);
date(int m, int d, int у);
void show_date();
};
// Ініціалізація рядком.
date::date(char *d)
{
sscanf(d, "%d%*c%d%*c%d" &month &day &year);
}
date::date(int m, int d, int у)
{
day = d;
month = m;
year = у;
}
void date::show_date() {
cout << month << "/" << day;
cout << "/" << year << "\n";
}
int main()
{
date ob1(12, 4, 2001), ob2("10/22/2001");
ob1.show_date();
ob2.show_date() ;
return 0;
}
В цій програмі об'єкт класу date можна ініціалізувати двома способами. Задавши місяць, день і рік у вигляді трьох цілих чисел або у вигляді рядка mm/dd/yyyy. Обидва способи застосовуються досить часто тому має сенс передбачити два різні конструктори для створення об'єктів класу date.
Цей приклад ілюструє основну ідею, яка лежить в основі перевантажених конструкторів: вони дозволяють вибирати спосіб створення об'єктів, який краще всіх відповідає конкретній ситуації. Наприклад, користувач може ввести дату у вигляді массива s. Цей рядок можна відразу використовувати для створення об'єкту класу date. Для цього абсолютно не вимагається перетворювати її в інший вигляд. Проте, якби конструктор date() би не був перевантажений, рядок довелося б розбити на три цілі числа.
int main()
{
char s[80];
cout << "Введіть нову дату: ";
сіn >> s;
date d(s);
d.show_date();
return 0 ;
}
В іншій ситуації користувачу зручніше ініціалізувати об'єкт класу date трьома цілими числами. Наприклад, якщо дата є результатом якихось обчислень, більш природньо створювати об'єкт класу date за допомогою конструктора date (int, int, int). Отже, у кожному конкретному випадку існує оптимальний варіант. Перевантажений конструктор забезпечує необхідну гнучкість класу, особливо необхідну при створенні бібліотек.
Параметри по замовчуванню
У мові C++ аргументам функції можна привласнювати значення, задані по замовчанню, якщо відповідний аргумент при виклику функції був пропущений. Значення за замовчанням задається за допомогою синтаксичної конструкції, яка дуже схожа на ініціалізацію змінної. Наприклад, наступний оператор оголошує, що функція myfunc () отримує один аргумент типу double, що за замовчанням приймає значення 0.0.
void myfunc (double d = 0.0)
Тепер функцію myfunc() можна викликати двома способами.
myfunc (198 .234) ; // Передача явного значення
myfunc O; // Функція використовує значення за замовчанням
При першому виклику параметр d отримує значення 198.234. Під час другого виклику параметр d автоматично приймає значення 0.0.
Параметри за замовчанням дозволяють справлятися із зростаючою складністю програм. У багатьох випадках функції містять більше параметрів, чим потрібно у конкретній ситуації. Таким чином, у кожному конкретному випадку достатньо вказати лише необхідні параметри, а не все відразу. Наприклад, багато функцій введення-вивдення використовують саме цей механізм.
Створюючи функції з аргументами за замовчанням, важливо пам'ятати, що їх значення можна задати лише один раз під час оголошення функції. У попередньому приклад значення за замовчанням було вказане в прототипі функції iputs (). Якщо спробувати задати нове (або навіть те ж саме значення) у визначенні функції iputs() компілятор видасть повідомлення про помилку. Хоча значення аргументів по замовчуванню перевизначити неможливо, можна задавати різні значення для кожної версії перевантаженої функції.
Всі параметри, що приймають значення за замовчанням, повинні розташовуватися правіше ніж звичайні аргументи. Наприклад, наступне визначення функції iputs() є неправильним:
void iputs(float a=1.23, int b, int z=0);
Почавши визначати параметри, що приймають значення за замовчанням, не можна перемішувати їх звичайними параметрами. Інакше кажучи, наступне оголошення є невірним.
int myfunc(float f, char *str, int i=10, int j);
Оскільки значення параметра i задається за замовчанням, параметр j також повинен мати значення за замовчанням.
Параметри конструктора теж можуть мати значення за замовчанням.