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

Последнее обновление: 24.03.2023

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

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

#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;
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850