Передача имен функций в качестве параметров

Функцию можно вызвать через указатель на нее. Для этого объявляется указатель соответствующего типа и ему с помощью операции взятия адреса присваивается адрес функции:

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;

}

Наши рекомендации