Передача имен функций в качестве параметров
Функцию можно вызвать через указатель на нее. Для этого объявляется указатель соответствующего типа и ему с помощью операции взятия адреса присваивается адрес функции:
void f(int a) { /* Тело функции */} // Определение функции
void (*pf) (int); // указатель на функцию
...
pf = &f; // указателю присваивается адрес функции
pf(10); // функция f вызывается через указатель pf
Указатели на функцию передаются в подпрограмму таким же образом, как и параметры других типов. Для того чтобы сделать программу легко читаемой, при описании указателей на функции используют переименование типов (typedef):
# include <iostream.h>
typedef void (*pf) (int); // Описание типа PF как указателя на функцию
// Функция f1 получает в качестве параметра указатель типа PF
void f1(PF pf) {
pf(5); // Вызов функции, переданной через указатель
}
void f( int i) { cout << i; }
int main ( ) {
f1(f);
return 0;
}
Параметры со значениями по умолчанию
Для того чтобы упростить вызов функции, в ее заголовке можно указать значения параметров по умолчанию. Эти параметры должны быть последними в списке и могут опускаться при вызове функции. Если при вызове параметр опущен, должны быть опущены и все параметры, стоящие за ним. В качестве значений параметров по умолчанию могут использоваться константы, глобальные переменные и выражения:
int f (int a, int b=0);
void f1 (int, int = 0, char*=0);
...
f(100);
f(a,1);
...
f1(a); f1(a,10); f1(a, 10, "Ivan");
Перегрузка функций
Часто бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя. Использование нескольких функций с одним и тем же именем, но с различными типами параметров называется перегрузкой функций.
Компилятор определяет, какую именно функцию требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением перегрузки. Тип возвращаемого функцией значения в разрешении не участвует.
Пример. Функции, возвращающие наибольшее из двух значений:
int max (int, int);
char * max (char*, char*);
int max (int, char*);
int max (char*, int);
// для параметров-строк вычисляется длина
void f(int a, int b, char *c, char *d) {
cout<<max(a,b)<<max(c,d)<<max(a,c)<<max(c,b);
}
При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции. Если точного соответствия не найдено, выполняются продвижения порядковых типов в соответствии с общими правилами, например bool и char в int, float в double и т.п. Далее выполняются стандартные преобразования типов, например int в double или указателей в void*. Следующим шагом является выполнение преобразований типа, заданных пользователем, а также поиск соответствий за счет переменного числа аргументов функций. Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.
Неоднозначность может проявиться:
- при преобразовании типа;
- при использовании параметров-ссылок;
- при использовании аргументов по умолчанию.
Пример. Неоднозначность при преобразовании типа:
# include <iostream.h>
float f(float i) {
cout << "fuction float f(float i)"<<endl; // endl переводит строку при использовании // потоков ввода-вывода
return i;
}
doudle f(double i) {
cout << "fuction double f(double i)"<<endl;
return i*2;
}
int main ( ) {
float x = 10.09;
double y = 10.09;
cout << f(x) << endl; // вызывается f(float )
cout << f(y) << endl; // вызывается f(double)
/* cout << f(10) << endl; − неоднозначность: как преобразовать 10: во float или double? */
return 0;
}
Для устранения этой неоднозначности требуется явное приведение типа для константы 10.
Пример неоднозначности при использовании параметров-ссылок: если одна из перегружаемых функций объявлена как Int f(int a, int b), а другая int f( int a, int &b), то компилятор не сможет узнать, какая из этих функций вызывается. так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке.
Пример. Неоднозначность при использовании аргументов по умолчанию:
# nclude <iostream.h>
int f(int a) { return a;}
int f(int a, int b =1) {return a*b;}
int main ( ) {
cout << f(10,2); // вызывается f(int, int)
// cout << f(10); - неоднозначность: что вызывается - f(int) или f(int, int)?
return 0;
}