Функция может возвращать указатель на другую функцию. Это может быть актуально, если имеется ограниченное количество вариантов - выполняемых функций, и надо выбрать одну из них. Но при этом набор вариантов и выбор из них определяется в промежуточной функции.
Рассмотрим простейший пример:
#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; }