Функция может возвращать указатель на другую функцию. Это может быть актуально, если имеется ограниченное количество вариантов - выполняемых функций, и надо выбрать одну из них. Но при этом набор вариантов и выбор из них определяется в промежуточной функции.
Рассмотрим простейший пример:
#include <iostream>
void goodmorning();
void goodevening();
void(*message(unsigned))();
int main()
{
void(*action)(){message(10)}; // указатель на выбранную функцию
// выполняем полученную функцию
action(); // Good Morning!
action = message(16); // получаем новую функцию
action(); // Good Evening!
}
void(*message(unsigned hour))()
{
if (hour > 15)
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:
void(*message(unsigned hour))()
Вначале указан тип, который возвращается функцией, которая возвращается из message, то есть тип void (функции goodmorning и goodevening имеют тип
void). Далее идет в скобках имя функции со списком параметров, то есть функция message принимает один параметр типа unsigned int: (*message(unsigned hour)).
После этого отдельно в скобках идет спецификация параметров функции, которая будет возвращаться из message. Поскольку функции goodmorning и goodevening не принимают никаких параметров, то
указываются пустые скобки.
Имя функции фактически представляет указатель на нее, поэтому в функции message мы можем возвратить нужную функцию, указав после оператора return ее имя.
Для получения указателя на функцию определяем переменную action:
void(*action)();
Эта переменная представляет указатель на функцию, которая не принимает параметров и имеет в качестве возвращаемого типа тип void, то есть она соответствует функциям goodmorning и goodevening.
Затем вызываем функцию message и получаем указатель на функцию в переменную action:
action = message(16);
Далее, используя указатель action, вызываем полученную функцию:
action();
Поскольку в функцию message передается число 16, то она будет возвращать указатель на функцию goodevening, поэтому при ее вызове на консоль будет выведено сообщение "Good Evening!"
Рассмотрим более сложный пример, в котором в зависимости от выбора пользователя выполняется та или иная арифметическая операция над двумя числами:
#include <iostream>
int add(int, int);
int subtract(int, int);
int multiply(int, int);
int(*select(int))(int, int);
int main()
{
int(*action)(int, int) {select(1)}; // получаем указатель на функцию add
std::cout << action(8, 5) << std::endl; // 13
action = select(2); // получаем указатель на функцию subtract
std::cout << action(8, 5) << std::endl; // 3
action = select(3); // получаем указатель на функцию multiply
std::cout << action(8, 5) << std::endl; // 40
}
int(*select(int choice))(int, int)
{
// возвращаем нужную функцию в зависимости от choice
switch (choice)
{
case 2:
return subtract;
case 3:
return multiply;
default:
return add;
}
}
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(). Она получает условный номер функции и возвращает указатель на функцию - по сути выбранную функцию.
Все выбираемые функции имеют прототип вида:
int add(int, int);
И прототип функции select должен соответствовать этому прототипу:
int (*select(int))(int, int)
То есть в начале идет тип - возвращаемый тип указателя на функцию, то есть int. Затем идет определение самой функции select - ее название со списком параметров
помещается в скобках - (*select(int)). Затем идет спецификация параметров функции, на которую определяется указатель. Так как функции add, subtract и multiply принимают два значения типа int,
то соответственно спецификация параметров выглядит следующим образом (int, int).
Для выбора нужной функции в select применяется конструкция switch-case:
int(*select(int choice))(int, int)
{
// возвращаем нужную функцию в зависимости от choice
switch (choice)
{
case 2:
return subtract;
case 3:
return multiply;
default:
return add;
}
}
В функции main() вызываем функцию select, передавая в нее определенное число и получая в качестве результата указатель на функцию:
int(*action)(int, int) {select(1)}; // получаем указатель на функцию add
После этого мы сможем вызвать функцию по указателю. Поскольку функция по указателю должна принимать два значения типа int, то мы их можем передать в вызываемую функцию и получить ее результат:
std::cout << action(8, 5) << std::endl; // 13
В примере выше сделано довольно просто - какое-бы число не будет передано в функцию select, она всегда возвратит указатель на некоторую функцию. Однако мы можем ограничить выбор:
#include <iostream>
int add(int, int);
int subtract(int, int);
int multiply(int, int);
int(*select(int))(int, int);
int main()
{
int(*action)(int, int) {select(14)}; // получаем указатель на функцию
if(action) // если не равно nullptr
{
std::cout << action(8, 5) << std::endl;
}
else
{
std::cout << "Undefined" << std::endl;
}
}
int(*select(int choice))(int, int)
{
int (*actions[])(int x, int y){ add, subtract, multiply };
// возвращаем нужную функцию в зависимости от choice
// возвращаем нужную функцию
if (choice >0 && choice<4)
return actions[choice - 1];
else
return nullptr;
}
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; }
В данном случае все доступные для выбора функции хранятся в массиве actions, который представляет массив указателей на функции. Если передан номер 1, 2 или 3, то возвращаем из
этого массива определенную функцию. Если переданный номер функции представляет другое число, то возвращаем значение nullptr:
if (choice >0 && choice<4)
return actions[choice - 1];
else
return nullptr;
Поскольку возвращается nullptr, то перед выполнением полученной функции указатель надо проверить на nullptr:
int(*action)(int, int) {select(14)}; // получаем указатель на функцию
if(action) // если не равно nullptr
{
std::cout << action(8, 5) << std::endl;
}
else
{
std::cout << "Undefined" << std::endl;
}