та надрукує: p>
** 1f 20 ** p>
Параметр за замовчуванням проходить перевірку типу під час
опису функції і обчислюється під час її виклику. Задавати параметр по
замовчуванням можливо тільки для останніх параметрів, тому p>
int f (int, int = 0, char * = 0);// ok p>
int g (int = 0, int = 0, char *);// помилка p>
int f (int = 0, int, char * = 0);// помилка p>
Зауважте, що в цьому контексті пробіл між * і =
є істотним (*= є операцією привласнення): p>
int nasty (char *= 0);// синтаксична помилка p>
Перевантаження Імен Функцій p>
Як правило, давати різних функцій різні імена - думка
хороша, але коли деякі функції виконують однакову роботу над об'єктами
різних типів, може бути більш зручно дати їм одне й те саме ім'я. Використання
одного імені для різних дій над різними типами називається
перевантаженням (overloading). Метод вже використовується для основних операцій C + +: у
складання існує тільки одне ім'я, +, але його можна застосовувати для складання
значень цілих, плаваючих та вказівних типів. Ця ідея легко розширюється на
обробку операцій, визначених користувачем, тобто, функцій. Щоб вберегти
програміста від випадкового повторного використання імені, ім'я може
використовуватися більш ніж для однієї функції тільки якщо воно спочатку описано як
переобтяжене. p>
Наприклад:
p>
overload print; p>
void print (int); p>
void print (char *); p>
Що стосується компілятора, єдине спільне, що мають
функції з однаковими іменами, це ім'я. Імовірно, вони в якомусь сенсі
схожі, але в цьому мова ні обмежує програміста, ні допомагає йому. Таким
чином, перевантажені імена функцій - це головним чином зручність запису. Це
зручність значно в разі функцій із загальноприйнятими іменами кшталт sqrt, print
і open. Коли ім'я семантично значуща, як це має місце для операцій на кшталт
+, * І <<і у випадку
конструкторів, це зручність стає суттєвим. p>
Коли викликається перевантажена f (), компілятор повинен
зрозуміти, до якої з функцій з ім'ям f слід звернутися. Це робиться шляхом
порівняння типів фактичних параметрів з типами формальних параметрів всіх
функцій з ім'ям f. Пошук функції, яку треба викликати, здійснюється за три
окремих кроки: p>
Шукати функцію відповідну точно, і використовувати її,
якщо вона знайдена; p>
Шукати відповідну функцію використовуючи вбудовані
перетворення і використовувати будь-яку знайдену функцію; і p>
Шукати відповідну функцію використовуючи перетворення, визначені
користувачем, і якщо безліч перетворень єдино, використовувати
знайдену функцію. p>
Наприклад:
p>
overload print (double), print (int); p>
void f (); p>
( p>
print (1); p>
print (1.0); p>
) p>
Правило точної відповідності гарантує, що f
надрукує 1 як ціле і 1.0 як число з плаваючою точкою. Нуль, char або short
точно відповідають параметру int. Аналогічно, float точно відповідає
double. p>
До параметрів функцій з перевантаженими іменами стандартні
C + + правила перетворення застосовуються не повністю. Перетворення, що можуть
знищити інформацію, не виконуються. Залишаються int в long, int в double, нуль
в long, нуль в double і перетворення покажчиків: нуль в покажчик, нуль в
void *, і покажчик на похідний клас у вказівник на базовий клас. p>
Ось приклад, в якому перетворення необхідно: p>
overload print (double), print (long); p>
void f (int a); p>
( p>
print (a); p>
) p>
Тут a може бути надрукована або як double, або як
long. Неоднозначність дозволяється явним перетворенням типу (або
print (long (a)) або print (double (a))). p>
При цих правилах можна гарантувати, що коли
ефективність та точність обчислень для використовуваних типів істотно
розрізняються, буде використовуватися найпростіший алгоритм (функція). p>
Наприклад:
p>
overload pow; p>
int pow (int, int); p>
double pow (double, double);// з p>
complex pow (double, complex);// з p>
complex pow (complex, int); p>
complex pow (complex, double); p>
complex pow (complex, complex); p>
Процес пошуку підходящої функції ігнорує unsigned і const. p>
незаданій Число Параметрів p>
Для деяких функцій неможливо поставити число і тип всіх
параметрів, які можна очікувати у виклику. Таку функцію описують завершуючи
список описів параметрів трьома крапками (...), що означає "і може бути,
ще якісь параметри ". p>
Наприклад: p>
int printf (char * ...); p>
Це задає, що у виклику printf повинен бути по меншій
б один параметр, char *, а інші можуть бути, а можуть і не бути. p>
Наприклад:
p>
printf ( "Hello, worldn "); p>
printf ( "Моє ім'я% s% sn", first_name, second_name); p>
printf ( "% d +% d =% dn", 2,3,5); p>
Така функція покладається на інформацію, яка
недоступна компілятору при інтерпретації її списку параметрів. У разі
printf () першим параметром є рядок формату, що містить спеціальні
послідовності символів, що дозволяють printf () правильно обробляти
інші параметри. % s означає "чекай параметра char *", а% d означає
"жди параметра int". Однак, компілятор цього не знає, тому він не
може переконатися в тому, що очікувані параметри мають відповідний тип. p>
Наприклад: p>
printf ( "Моє ім'я% s% sn", 2); p>
відкомпілює і в кращому випадку призведе до якої-небудь
дивного вигляду видачі. p>
Очевидно, якщо параметр не був описаний, то у компілятора
немає інформації, необхідної для виконання над ним перевірки типу і
перетворення типу. У цьому випадку char або short передаються як int, а float
передається як double. Це не обов'язково те, чого чекає користувач. p>
Надмірне використання крапок, на зразок wild (...),
цілком вимкнути перевірку типів параметрів, залишаючи програміста відкритим
перед безліччю неприємностей, які добре знайомі програмістам на C. У
добре продуманій програмі потрібно щонайбільше кілька функцій, для
яких типи параметрів не визначені повністю. Для того, щоб подбати про
перевірки типів, можна використовувати перевантажені функції та функції з
параметрами за замовчуванням у більшості тих випадків, коли інакше довелося б
залишити типи параметрів незаданій. Многоточие необхідно тільки якщо
змінюються і число параметрів, і тип установки. Найбільш звичайне застосування багатокрапки
в завданні інтерфейсу з функціями C бібліотек, які були визначені в той
час, коли альтернативи не було: p>
extern int fprintf (FILE *, char * ...);// з p>
extern int execl (char * ...);// з p>
extern int abort (...);// з p>
Розберемо випадок написання функції помилок, яка
отримує один цілий параметр, який вказує серйозність помилки, після якого
йде довільну кількість рядків. Ідея полягає в тому, щоб складати повідомлення
про помилку за допомогою передачі кожного слова як окремого строкового параметра:
p>
void error (int ...); p>
main (int argc, char * argv []) p>
( p>
switch (argc) ( p>
case 1: p>
error (0, argv [0], 0); p>
break; p>
case 2: p>
error (0, argv [0], argv [1], 0); p>
default: p>
error (1, argv [0], "с", dec (argc-1), "параметрами", 0); p>
) p>
) p>
Функцію помилок можна визначити так: p>
# include p>
void error (int n ...) p>
/* p>
"n" з наступним списком char *, що закінчуються
нулем p>
*/ p>
( p>
va_list ap; p>
va_start (ap, n);// розкрутка
arg p>
for (;;) ( p>
char * p = va_arg (ap, char *); p>
if (p == 0) break; p>
cerr <
) p>
va_end (ap);// очищення
arg p>
cerr << "n"; p>
if (n) exit (n); p>
) p>
Перший з va_list визначається і ініціалізується викликом
va_start (). Макрос va_start отримує ім'я va_list "а й ім'я останнього
формального параметра як параметри. Макрос va_arg використовується для вибору
неіменованних параметрів по порядку. При кожному зверненні програміст повинен
задати тип; va_arg () припускає, що був переданий фактичний параметр, але
звичайно способу переконатися в цьому немає. Перед поверненням з функції, в якій був
використаний va_start (), повинен бути викликаний va_end (). Причина в тому, що va_start ()
може змінити стек так, що не можна буде успішно здійснити повернення; va_end ()
анулює всі ці зміни. p>
Покажчик на Функцію h2>
З функцією можна робити тільки дві речі: викликати її і
брати її адресу. Покажчик, отриманий взяттям адреси функції, можна потім
використовувати для виклику цієї функції. p>
Наприклад:
p>
void error (char * p) (/ * ... * /) P>
void (* efct) (char *);// покажчик на функцію p>
void f () p>
( p>
efct = &error;// efct вказує на error p>
(* efct) ( "error");// виклик error через efct p>
) p>
Щоб викликати функцію через вказівник, наприклад, efct,
треба спочатку цей покажчик разименовать, * efct. Оскільки операція виклику
функції () має більш високий пріоритет, ніж операція разименованія *, то
не можна писати просто * efct ( "error"). Це означає * efct ( "error"),
а це помилка в типі. Те ж відноситься і до синтаксису описів. P>
Зауважте, що у покажчиків на функції типи параметрів
описуються точно також, як і в самих функціях. У привласнення покажчика
повинна дотримуватися точну відповідність повного типу функції. p>
Наприклад: p>
void (* pf)(char *);// покажчик на void (char *) p>
void f1 (char *);// void (char *) p>
int f2 (char *);// int (char *) p>
void f3 (int *);// void (int *) p>
void f () p>
( p>
pf = &f1;// ok p>
pf = &f2;// помилка: не підходить повертається тип p>
pf = &f3;// помилка: не підходить тип параметра p>
(* pf) ( "asdf");// ok p>
(* pf) (1);// помилка: не підходить тип параметра p>
int i = (* pf) ( "qwer");// помилка: void
присвоюється int "у p>
) p>
Правила передачі параметрів для безпосередніх викликів
функції і для викликів функції через покажчик одні й ті ж. p>
Часто, щоб уникнути використання будь-якого
неочевидного синтаксису, буває зручно визначити ім'я типу
покажчик-на-функцію. p>
Наприклад:
p>
typedef int (* SIG_TYP) ();// з p>
typedef void (* SIG_ARG_TYP); p>
SIG_TYP signal (int, SIG_ARG_TYP); p>
Буває часто корисний вектор вказівників на функцію.
Наприклад, система меню для мого редактора з мишею * 4 реалізована за допомогою
векторів покажчиків на функції для представлення дій. Детально цю систему
тут описати не вийде, але загальна ідея: p>
typedef void (* PF )(); p>
PF edit_ops [] = (//операції редагування p>
cut, paste, snarf, search p>
); p>
PF file_ops [] = (//керування файлом p>
open, reshape, close, write p>
); p>
Потім визначаємо і ініціалізіруем покажчики, що визначають
дії, обрані в меню, яке пов'язане з кнопками (button) миші: p>
PF * button2 = edit_ops; p>
PF * button3 = file_ops; p>
У повній реалізації для визначення кожного пункту меню
потрібно більше інформації. Наприклад, десь повинна зберігатися рядок, що задає
текст, який висвічується. При використанні системи значення кнопок миші
часто міняється залежно від ситуації. Ці зміни здійснюються
(частково) за допомогою зміни значень покажчиків кнопок. Коли користувач
обирає пункт меню, наприклад пункт 3 для кнопки 2, виконується пов'язане з ним
дію: p>
(button2 [3 ])(); p>
Один зі способів оцінити величезну міць покажчиків на
функції - це спробувати написати таку систему не використовуючи їх. Меню можна
міняти в ході використання програми, вносячи нові функції в таблицю дій.
Під час виконання можна також легко сконструювати нове меню. p>
Покажчики на функції можна використовувати для завдання
поліморфних підпрограм, тобто підпрограм, які можуть застосовуватися до
об'єктам багатьох різних типів: p>
typedef int (* CFT) (char *, char *); p>
int sort (char * base, unsigned n, int sz, CFT cmp) p>
/* p>
сортує "n" елементів вектора
"base" p>
у зростаючому порядку p>
за допомогою функції порівняння, що вказується "cmp". p>
Розмір елементів "sz". p>
Дуже неефективний алгоритм: бульбашкова сортування p>
*/ p>
( p>
for (int i = 0; iname, Puser (q) -> name); p>
) p>
int cmp2 (char * p, char * q)// Порівнює числа dept p>
( p>
return Puser (p) -> dept-Puser (q) -> dept; p>
) p>
Ця програма сортує і друкує: p>
main () p>
( p>
sort ((char *) heads, 6, sizeof (user), cmp1); p>
print_id (heads, 6);// в алфавітному порядку p>
cout << "n"; p>
sort ((char *) heads, 6, sizeof (user), cmp2); p>
print_id (heads, 6);// по порядку підрозділів p>
) p>
Можна взяти адреса inline-функції, як, втім, і адреса
перевантаженій функції. p>
Список літератури h2>
Для підготовки даної роботи були використані матеріали
з сайту http://www.realcoding.net
p>