ООП 07 лек перевантаження функцій
Перевантажування ФУНКЦІЙ ________________________________________________________________ 1
Призначення перевантаження __________________________________________________________________ 1 Декорування компілятором імен функцій ________________________________________________ 2 Перевантаження функцій _____________________________________________________________________ 2 Перевантаження конструкторів ________________________________________________________________ 3 Неоднозначність перевантаження _____________________________________________________________ 3
Перевантаження функцій - це один з типів поліморфізму, що забезпечується C ++. У C ++ кілька функцій можуть мати одне і те ж ім'я. У цьому випадку функція, що ідентифікується цим ім'ям, називається перевантаженою. Перевантажити можна тільки функції, які відрізняються або типом, або числом своїх аргументів. Перевантажити функції, які відрізняються тільки типом значення, що повертається,
не можна. Перенавантажувані функції дають можливість спростити програми, допускаючи звернення до одного імені для виконання близьких за змістом дій.
Щоб перевантажити деякуфункцію, потрібно просто оголосити, а потім визначити всі необхідні варіанти її виклику. Компілятор автоматично вибере правильний варіант виклику на підставі числа і типу використовуваних аргументів. приклад:
// прототипи перевантажених функцій
// функція отримує і повертає ціле значення
float abs (float);
// функція оперує з дійсним числом одинарної точності
double abs (double);
// функція оперує з дійсним числом подвійної точності
float mF. F = -25.0f; double mD. D = -2.55;
// виклик перевантаженої функції abs (int)
// виклик перевантаженої функції abs (float)
// виклик перевантаженої функції abs (double)
cout <<'|' < cout<<"abs(int)\t"; return a <0. –a. a ; float abs (float a) cout<<"abs(float)\t"; return a <0. –a. a ; double abs (double a) cout<<"abs(double)\t"; return a <0. –a. a ; При виконанні програма виводить на екран: мента. Так, в даному прикладі здійснюється перевантаження функції abs (). що спрощує програму. Залежно від переданого аргументу викликається потрібний варіант функції. Цей приклад наочно демонструє, як використання перевантаження може спростити код, надаючи програмісту можливість користуватися однією функцією, якої передаються аргументи різного типу. Зрозуміло, можливість використання перевантаження висуває підвищені вимоги до компілятору. Декорування компілятором імен функцій Щоб реалізувати концепцію перевантаження, розробникам компіляторів C ++ довелося ввести декорування імен. Останнє означає, що всі функції в коді програми отримують від компілятора імена, грунтуючись на імені, заданому програмістом, і кількості та типах аргументів. Різні компілятори роблять це дещо відмінним один від одного чином. Тут ми опишемо, як це робить компілятор фірми Inprise (раніше - фірма Borland). Спочатку йде символ "@" і ім'я класу, потім символ "@" і ім'я функції, У всіх ідентифікаторів різниться регістр букв. Потім слід послідовність символів "@q", починаючи з якої йдуть кодовані позначення параметрів функції. Для позначення покажчиків і посилань до кодів вбудованих типів додаються букви "р" і "r", відповідно. Наприклад, якщо дано таке визначення класу: void SetVal (void); void SetVal (int); void SetVal (int *. double); void SetVal (int . int. float); компілятор Borland C ++ згенерує такі імена функцій: Звідси видно, що в декоруванні імен повертається функцією значення не бере. Саме з цієї причини не можна перевантажувати функції, які відрізняються тільки типом значення, що повертається. Розібратися з тим, як декорує імена конкретний компілятор досить легко. Досить зажадати від нього видати асемблерний еквівалент тексту програми. int func (int); int func (int ); призведе до помилки, так як з точки зору перевантаження вони вважаються однаковими. Аргументи функції, пов'язані з деякого типу, модифіковані const або volatile. не розглядаються як відмінні від базового типу з точки зору перевантаження. Покажчики на const - і volatile -об'єкти також не розглядаються як відмінні від покажчиків на базовий тип з точки зору перевантаження. Однак механізм перевантаження може розрізняти посилання, які мають модифікатори const і volatile. і посилання на базовий тип. Разом з тим на перевантажені функції накладаються кілька обмежень: будь-які дві перевантажені функції повинні мати різні списки параметрів; перевантаження функцій з однаковими списками аргументів на основі лише типу повертаються ними значень неприпустима; функції-члени не можуть бути перевантажені виключно на основі того, що одна з них є статичною, а інша - ні; все enum -типи даних розглядаються як різні і можуть використовуватися для розрізнення перевантажених функцій; типи "масив (чогось)" і "покажчик (на щось)" розглядаються як ідентичні з точки зору перевантаження. typedef -Визначення не впливають на механізм перевантаження, так як вони не вводять нових типів даних, а визначають лише синоніми для існуючих типів. Наприклад, таке визначення typedef char * PSTR; не дозволить компілятору розглядати дві наведені нижче функції void SetVal (char * sz); void SetVal (PSTR sz); Для багатовимірних масивів друга і наступні розмірності розглядаються як частина типу даних. Тому вони можуть використовуватися для розрізнення перевантажених функцій. Наприклад, цілком припустимі наступні визначення: void SetVal (char S []); void SetVal (char S [] [4]); Найчастіше перевантаження застосовується при створенні перевантажених конструкторів (перевантажувати деструктор не можна!). Метою цієї перевантаження є бажання надати користувачеві якомога більше варіантів створення представників класу. Насправді ми вже неодноразово зустрічалися з перевантаженням конструкторів, хоча і не говорили про це. Якщо клас надає конструктор з параметрами і конструктор за замовчуванням, ми вже маємо справу з перевантаженням конструкторів. Як ми вже знаємо, конструктор за замовчуванням необхідний при виділенні динамічної пам'яті масиву об'єктів (бо динамічний масив об'єктів не може бути инициализирован). Інший випадок, коли виникає необхідність в перевантаженні конструкторів, - бажання забезпечити користувача конструкторами перетворення типу. знайдено точну відповідність; виконано тривіальне перетворення; виконано перетворення цілочисельних типів; існує стандартне перетворення до бажаного типу аргументу; існує певна програмістом перетворення (оператор перетворення або конструктор) до необхідного типу аргументу; були знайдені аргументи, представлені трьома крапками. Компілятор створює сукупність функцій-кандидатів на відповідність для кожного аргументу. Потім для кожного аргументу будується сукупність функцій, відповідних найкращим чином. нако- нець, визначається функція, яка найкращим чином відповідає викликом, як перетин всіх цих сукупностей. Якщо це перетин містить кілька функцій, - перевантаження неоднозначна, і компілятор генерує повідомлення про помилку. Ще одне важливе зауваження. Неоднозначність перевантажених функцій не може бути визначена, поки не зустрінеться виклик функції. Функція з n аргументами за замовчуванням, з точки зору відповідності аргументів, розглядається як сукупність з n + 1 функцій, кожна з яких відрізняється від попередньої завданням одного додаткового аргументу. Три крапки (.) Діє як довільний символ: воно відповідає будь-якому заданому аргументу. Це може служити джерелом неоднозначності при виборі перевантаженої функції. Все сказане вище відносилося до всіх перевантаженим функцій, безвідносно до того, чи є вони функціями-членами чи ні. Розглянемо тепер специфіку перевантажених функцій-членів. Функції-члени класу розглядаються по-різному в залежності від того, оголошені вони статичними чи ні, тому що нестатичні функції мають неявний аргумент, через який передається покажчик this. При визначенні функції, яка найкращим чином відповідає викликом, для нестатичних функцій-членів розглядаються тільки ті перевантажені функції-члени, у яких прихований покажчик this відповідає типу об'єкта, який передано функції при виклику. На відміну від інших аргументів при спробі встановити відповідність аргументу з покажчиком this ніякі перетворення не виробляються. Конструктор, оголошений з специфікатором explicit. не приймає участі в неявних перетвореннях типу. Він може бути використаний тільки в тому випадку, коли ніякого перетворення типу аргументів не потрібно. Наприклад, для класу, оголошеного вище: