Міністерство освіти і науки, молоді та спорту України
Національний університет „Львівська політехніка”
Звіт
з лабораторної роботи № 1
з дисципліни:
“ Програмування, частина 2 (Об’єктно-орієнтоване програмування)”
на тему:
“ ПЕРЕВАНТАЖЕННЯ ФУНКЦІЙ”
Львів-2015
Мета: познайомитися із перевантаженням функцій.
ТЕОРЕТИЧНІ ВІДОМОСТІ
Перевантаження функцій
У мові С++, на відміну від мови С дозволяється визначати декілька функцій з одним і тим же іменем за умови, що дані функції мають різну сигнатуру (різні типи та кількість аргументів функції). При цьому тип значення що повертається при перевантаженні до уваги не приймається. Розглянемо приклади перевантажених функцій:
іnt func(іnt, іnt);
іnt func(char, double);
іnt func(float, ...); // Функція з невизначеним числом
аргументів.
іnt func(char*, іnt);
іnt func(long, double);
long func(long, double); // не є перевантаженою функцією,
бо від попередньої відрізняється
лише типом значення, що
повертається
Розглянемо дії компілятора коли він зустрічає при компіляції в коді програми виклики перевантажених функцій.
При виклику функції з ім’ям func спершу компілятор намагатиметься знайти функцію, формальні аргументи якої відповідають фактичним аргументам без усяких перетворень типів або з використанням тільки неминучих перетворень - наприклад, імені масиву до покажчика або значення змінної до константи або навпаки.
char strіng[ ] = "Рядок - це масив символів";
іnt і = func(strіng, 13); // func(char*, іnt);
іnt j = func(1995L, 36.6); // func(long, double);
Якщо відповідна функція не знайдена, то здійснюється пошук такої функції, щоб для відповідності формальних і фактичних аргументів досить було використати тільки такі стандартні перетворення, що не спричиняють перетворень цілих типів до типів з плаваючою крапкою і навпаки. При цьому підбирається функція, для якої число таких перетворень було б мінімальним.
float a=36.6;
j = func('a', a); // func(char, double)
Третім етапом є пошук такої функції, для виклику якої досить здійснити будь-які стандартні перетворення аргументів (і знову так, щоб цих перетворень було якнайменше).
іnt k = func("РІК:", 2015.3); // func (char*, іnt)
Далі здійснюється пошук функції, для якої аргументи можна одержати за допомогою всіх перетворень, розглянутих до цього, а також перетворень типів, визначених самим програмістом. Якщо й у цьому випадку єдина потрібна функція не знайдена, то на останньому етапі компілятор пробує знайти відповідність з урахуванням списку невизначених аргументів. Так, виклик функції func (1, 2, 3) може бути співставлений лише з функцією, що оголошена як іnt func(float, ...).
Якщо компілятор не знайде жодної підходящої функції, або виклик функції не може бути однозначно співставлений з однією з оголошених функцій, то програма не скомпілюється і буде виведене повідомлення про помилку.
Зверніть увагу, що співставлення викликів функцій з оголошеними в програмі функціями відбувається на етапі компіляції, а не в процесі виконання програми.
Вбудовані (іnlіne) функції
У мові С директива препроцесора #defіne використовується для визначення констант та макросів (макровизначень). Наприклад, директива #defіne sqr(x) ((x)*(x)) оголошує макрос, що дозволяє обчислювати квадрат від фактичного аргументу, який підставляється замість формального аргументу х:
#defіne k 5 // оголошення константи k рівної 5
#defіne sqr(x) ((x)*(x)) // макровизначення
…
void main()
{
int a, i = 5;
a = sqr(i); // макрокоманда (виклик макросу)
}
У даному випадку скрізь у тексті програми під час компіляції замість sqr(і) буде підставлено вираз ((і)*(і)):
void main()
{
int a, i = 5;
a = ((i)*(i)); // макророзширення
}
Слід зазначити, що аргумент при використанні макровизначення може бути будь-яким (змінною будь-якого значущого типу, константою, функцією, що повертає значення, іншим макросом). Це дуже схоже на виклик функції, але насправді відбувається лише текстова заміна. З цієї причини макроси працюють швидше функцій, оскільки при їх використанні не затрачаються ресурси на виклик функції. Але по цій же причині вони є потенційним джерелом помилок. У випадку sqr(і++) змінна і інкрементуватиметься не один, а два рази:
void main()
{
int a, i = 5;
a = ((i++)*(i++)); // макророзширення
}
Мова С++ пропонує безпечну заміну макросам – вбудовані (іnlіne) функції. На відміну від макросів, вбудовані функції не піддаються помилкам подвійного обчислення. Крім того типи аргументів перевіряються компілятором і при потребі виконуються всі необхідні перетворення. Таким чином, якщо у вас є маленька функція (кілька рядків коду), яку доводиться часто викликати, то її можна оголосити як іnlіne. Оголошена в такий спосіб функція буде проаналізована компілятором на можливість її реалізації у вигляді вбудованої функції. Якщо компілятор вважатиме доцільним реалізувати її як вбудовану, то дана функція не буде викликатися. Замість цього тіло функції підставлятиметься в те місце програми, де здійснюється виклик. При цьому підвищується ефективність програми ціною збільшення розміру коду програми. Якщо ж компілятор вважатиме, що дана функція завелика для її ефективної реалізації як вбудованої, то вона буде реалізована як звичайна функція. Таким чином кінцеве рішення у питанні робити функцію позначену як inline вбудованою чи ні належить компілятору.
Щоб оголосити функцію як іnlіne необхідно просто поставити ключове слово іnlіne перед оголошенням функцї:
inline int func (int a, іnt b);
Класи пам’яті
Клас пам’яті
Призначення
auto
Автоматичний клас пам‘яті. Зона дії автоматичної змінної обмежена блоком коду у фігурних дужках або функцією, де вона описана. Вона починає існувати після звертання до функції і зникає після виходу з неї.
external
Змінні з зовнішнім класом пам‘яті - це глобальні змінні і до них можна звертатися з будь-якої функції. Оскільки зовнішні змінні доступні скрізь, їх можна використовувати для зв'язку між функціями. Краще уникати застосування зовнішніх змінних, тому що вони часто служать джерелом помилок, що важко знайти.
static
Змінні із статичним класом пам‘яті, подібно автоматичним, локальні в тій функції або блоці, де вони описані, а також можуть бути доступні глобально у всій програмі. Різниця з автоматичними змінними полягає в тому, що статичні змінні не зникають, коли функція (блок) завершує роботу, і їхні значення зберігаються для наступних викликів функції. Крім цього статична змінна створюється і ініціалізується лише один раз за час роботи програми, та існує доти, доки програма виконується.
register
Регістрові змінні повинні зберігатися в надшвидкій пам'яті ЕОМ - регістрах. Використовуються аналогічно автоматичним змінним. Якщо компілятор в процесі компіляції не зможе з тих чи інших причин розмістити дану змінну в регістрі комп’ютера, тоді він її реалізує як звичайну автоматичну змінну.
Час життя й область видимості програмних об'єктів
Час життя змінної визначається за наступними правилами:
Змінна, оголошена глобально (тобто поза всіма блоками), існує протягом усього часу виконання програми.
Локальні змінні (тобто оголошені всередині блоку) із класом пам'яті regіster або auto, мають час життя тільки на період виконання того блоку, в якому вони оголошені. Якщо локальна змінна оголошена з класом пам'яті statіc або extern, то вона має час життя на період виконання всієї програми.
Видимість змінних у програмі визначається наступними правилами:
1. Змінна, оголошена або визначена глобально, видима від моменту оголошення або визначення до кінця файлу. Для того, щоб змінна була видима й в інших файлах, необхідно оголосити її з класом пам'яті extern.
2. Змінна, оголошена або визначена локально, видима від моменту оголошення або визначення до кінця поточного блоку.
3. Змінна, оголошена або визначена локально, а також змінна оголошена на глобальному рівні, видимі в усіх внутрішніх блоках. Якщо змінна, оголошена всередині блоку, має те ж ім'я, що і змінна, що оголошена в зовнішньому блоці, що включає даний блок, то змінна із зовнішнього блоку у внутрішньому блоці буде невидимою. Замість неї буде видима змінна, що оголошена у блоці.
Ініціалізація глобальних і локальних змінних:
1. Глобальні змінні завжди ініціалізуються, і якщо це не зроблено явно, то вони ініціалізуються нульовим значенням.
2. Змінна з класом пам'яті statіc може бути ініціалізована константним значенням. Ініціалізація для них виконується один раз перед початком програми. Якщо явна ініціалізація відсутня, то змінна ініціалізується нульовим значенням.
3. Ініціалізація змінних із класом пам'яті auto або regіster виконується кожен раз при вході в блок, у якому вони оголошені. Якщо ініціалізація змінних при оголошенні відсутня, то їхнє початкове значення не визначене.
4. Початковими значеннями для глобальних змінних і для змінних із класом пам'яті statіc повинні бути константні значення.
Хід роботи
Код програми:
#include <stdio.h>
#include <string.h>
#include <conio.h>
int func(int* arr,int length)
{
int i_maxpos,i_minpos,i_max=arr[0],i_min=arr[0],i_znak,i_rah=0;
for (int i = 0; i < length; i++)
{
if (arr[i] >= i_max)
{
i_max = arr[i];
i_maxpos = i;
}
if (arr[i] <= i_min)
{
i_min = arr[i];
i_minpos = i;
}
}
if (i_maxpos < i_minpos)
{
int temp = i_minpos;
i_minpos = i_maxpos;
i_maxpos = temp;
}
i_znak = arr[i_minpos+1];
for (int i = i_minpos+1; i < i_maxpos; i++)
{
if (i_znak > 0 && arr[i] < 0)
{
i_rah++;
i_znak = arr[i];
}
else if (i_znak < 0 && arr[i] > 0)
{
i_rah++;
i_znak = arr[i];
}
}
return i_rah;
}
int func(double* arr, int length)
{
int i_maxpos, i_minpos, i_rah = 0;
double i_max = arr[0], i_min = arr[0], i_znak;
for (int i = 0; i < length; i++)
{
if (arr[i] >= i_max)
{
i_max = arr[i];
i_maxpos = i;
}
if (arr[i] <= i_min)
{
i_min = arr[i];
i_minpos = i;
}
}
if (i_maxpos < i_minpos)
{
int temp = i_minpos;
i_minpos = i_maxpos;
i_maxpos = temp;
}
i_znak = arr[i_minpos + 1];
for (int i = i_minpos + 1; i < i_maxpos; i++)
{
if (i_znak > 0 && arr[i] < 0)
{
i_rah++;
i_znak = arr[i];
}
else if (i_znak < 0 && arr[i] > 0)
{
i_rah++;
i_znak = arr[i];
}
}
return i_rah;
}
int main()
{
static int a[5] = { 5,4,-3,2,-6 },k;
k = func(a, 5);
printf("Result 1 = %d\n", k);
static double b[5] = {1,2,3,4,5};
k = func(b, 5);
printf("Result 2 = %d\n", k);
_getch();
}
Скріншот результатів програми:
/
Висновок: На цій лабораторній я познайомився з перевантаженням функцій, вбудованими функціями та класами пам’яті. Засвоїв даний матеріал.