Указатель на функцию как возвращаемое значение


Функция может возвращать указатель на другую функцию. Это может быть актуально, если имеется ограниченное количество вариантов - выполняемых функций, и надо выбрать одну из них. Но при этом набор вариантов и выбор из них определяется в промежуточной функции.

Рассмотрим простейший пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

void goodmorning();
void goodevening();
void(*message(int))();

int main()
{
void(*action)(); // указатель на выбранную функцию
action = message(15);
action(); // выполняем полученную функцию
return 0;
}

void(*message(int hour))()
{
if (hour > 12)
return goodevening;
else
return goodmorning;
}
void goodmorning()
{
std::cout << "Good Morning!" << std::endl;
}
void goodevening()
{
std::cout << "Good Evening!" << std::endl;
}
Здесь определена функция message, которая в зависимости от переданного числа возвращает одну из двух функций goodmorning или goodevening. Рассмотрим объявление функции message:

1
void(*message(int hour))()
Вначале указан тип, который возвращается функцией, которая возвращается из message, то есть тип void (функции goodmorning и goodevening имеют тип void). Далее идет в скобках имя функции со списком параметров, то есть функция message принимает один параметр типа int: (*message(int hour)). После этого отдельно в скобках идет спецификация параметров функции, которая будет возвращаться из message. Поскольку функции goodmorning и goodevening не принимают никаких параметров, то указываются пустые скобки.

Имя функции фактически представляет указатель на нее, поэтому в функции message мы можем возвратить нудную функцию, указав после оператора return ее имя.

Для получения указателя на функцию определяем переменную action:

1
void(*action)();
Эта переменная представляет указатель на функцию, которая не принимает параметров и имеет в качестве возвращаемого типа тип void, то есть она соответствует функциям goodmorning и goodevening.

Затем вызываем функцию message и получаем указатель на функцию в переменную action:

1
action = message(15);
Далее, используя указатель action, вызываем полученную функцию:

1
action();
Поскольку в функцию message передается число 15, то она будет возвращать указатель на функцию goodevening, поэтому при ее вызове на консоль будет выведено сообщение "Good Evening!"

Рассмотрим более сложный пример, в котором в зависимости от выбора пользователя выполняется та или иная арифметическая операция над двумя числами:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>

int add(int, int);
int subtract(int, int);
int multiply(int, int);
int(*select())(int, int);

int main()
{
int x = 8;
int y = 5;
std::cout << "x = " << x << "\ty = " << y << std::endl;
std::cout << "1: Add" << std::endl;
std::cout << "2: Subtract" << std::endl;
std::cout << "3: Multiply" << std::endl;
std::cout << "4: Exit" << std::endl;

int(*action)(int, int); // указатель на выбранную функцию
int result; // результат функции
while (1)
{
action = select(); // получаем указатель на функцию
if (action == NULL)
break;
result = action(x, y); // выполняем функцию
std::cout << "Result: " << result << std::endl;
}
std::cout << "The End" << std::endl;

return 0;
}

int(*select())(int, int)
{
int choice; // выбранный пункт
// массив указателей на функции, которые будут возвращаться
int (*actions[])(int x, int y) = { add, subtract, multiply };
// выбираем действие по номеру
std::cout << "Enter action (1, 2, 3, 4): ";
std::cin >> choice;
// возвращаем нужную функцию
if (choice >0 && choice<4)
return actions[choice - 1];
else
return NULL;
}
int add(int x, int y)
{
return x + y;
}
int subtract(int x, int y)
{
return x - y;
}
int multiply(int x, int y)
{
return x * y;
}
В данной программе мы предполагаем, что пользователь должен выбрать для выполнения одну из трех функций: add, subtract, multiply. И выбранная функция будет выполнять определенное действие над двумя числами x и y.

Сам выбор происходит в функции select(). Она возвращает указатель на функцию - по сути выбранную функцию.

Все выбираемые функции имеют прототип вида:

1
int add(int, int);
И прототип функции select должна соответствовать этому прототипу:

1
int (*select())(int, int)
То есть в начале идет тип - возвращаемый тип указателя на функцию, то есть int. Затем идет определение самой функции select - ее название со списком параметров помещается в скобках - (*select()). Затем идет спецификация параметров функции, на которую определяется указатель. Так как функции add, subtract и multiply принимают два значения типа int, то соответственно спецификация параметров выглядит следующим образом (int, int).

Для хранения всех действий в функции select определен массив указателей на функции actions:

1
int (*actions[])(int x, int y) = { add, subtract, multiply };
С помощью введенного с клавиатуры числа определяем номер нужного действия, которое надо выполнить. Если номер меньше 1 или больше 3, то возвращается константа NULL.

В главной функции main() в бесконечном цикле вызываем функцию select, получая в качестве результата указатель на функцию:

1
action = select();
И если указатель не равен NULL, то после этого мы сможем вызвать функцию по указателю. Поскольку функция по указателю должна принимать два значения типа int, то мы их можем передать в вызываемую функцию и получить ее результат:

1
result = action(x,y);
Консольный вывод работы программы:

x = 8 y = 5
1: Add
2: Subtract
3: Multiply
4: Exit
Enter action (1, 2, 3, 4): 1
Result: 13
Enter action (1, 2, 3, 4): 2
Result: 3
Enter action (1, 2, 3, 4): 4
The End