← Назад к вопросам

Как объявить указатель на функцию?

1.7 Middle🔥 181 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Объявление указателей на функции в C/C++

Указатель на функцию — это переменная, которая хранит адрес функции в памяти. Это мощный инструмент для реализации callback'ов, стратегий и функциональности более высокого порядка.

Базовое объявление указателя на функцию

Синтаксис может быть запутанным, поэтому разберёмся пошагово:

// Обычная функция
int add(int a, int b) {
    return a + b;
}

// Указатель на функцию
int (*ptr)(int, int) = &add;

// Вызов через указатель (оба способа работают)
int result1 = ptr(3, 4);         // Способ 1
int result2 = (*ptr)(3, 4);      // Способ 2 (более явный)

Как читать объявление int (*ptr)(int, int):

  1. ptr — имя переменной
  2. (int, int) — параметры функции
  3. int — тип возвращаемого значения
  4. * — это указатель

Шаг за шагом: разбор синтаксиса

// Шаг 1: тип возвращаемого значения
int ...

// Шаг 2: это указатель на функцию
int (*ptr)...

// Шаг 3: параметры функции
int (*ptr)(int, int)

// Результат: указатель на функцию, принимающую два int и возвращающую int

Объявление без инициализации

// Только объявление (без инициализации)
int (*operation)(int, int);

// Позже инициализируем
operation = add;

// Вызываем
int result = operation(10, 20);

Void функции

// Указатель на функцию, возвращающую void
void (*print_func)(const char*) = nullptr;

void hello(const char* name) {
    std::cout << "Hello, " << name << std::endl;
}

int main() {
    print_func = hello;
    print_func("World");  // Выведет: Hello, World
}

Массив указателей на функции

int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

int main() {
    // Массив из 3 указателей на функции
    int (*operations[3])(int, int) = {
        add,
        subtract,
        multiply
    };
    
    std::cout << operations[0](10, 5) << std::endl;  // 15 (add)
    std::cout << operations[1](10, 5) << std::endl;  // 5  (subtract)
    std::cout << operations[2](10, 5) << std::endl;  // 50 (multiply)
}

Передача указателя на функцию в качестве параметра

// Функция, принимающая указатель на функцию
int apply_operation(int a, int b, int (*op)(int, int)) {
    return op(a, b);
}

int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }

int main() {
    std::cout << apply_operation(3, 4, add) << std::endl;       // 7
    std::cout << apply_operation(3, 4, multiply) << std::endl;  // 12
}

Возврат указателя на функцию

// Функция, возвращающая указатель на функцию
int (*get_operation(char op))(int, int) {
    switch (op) {
        case '+':
            return add;
        case '*':
            return multiply;
        default:
            return add;
    }
}

int main() {
    auto operation = get_operation('+');
    std::cout << operation(3, 4) << std::endl;  // 7
}

Примечание: такой синтаксис очень сложен. Лучше использовать using или typedef.

Упрощение синтаксиса через typedef

// Объявляем тип для указателя на функцию
typedef int (*MathOperation)(int, int);

int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }

int apply(int x, int y, MathOperation op) {
    return op(x, y);
}

int main() {
    MathOperation ops[2] = {add, multiply};
    
    std::cout << apply(3, 4, ops[0]) << std::endl;  // 7
    std::cout << apply(3, 4, ops[1]) << std::endl;  // 12
}

Современный C++: using вместо typedef

// C++11 и позже (более читаемо)
using MathOperation = int(*)(int, int);

int add(int a, int b) { return a + b; }

int main() {
    MathOperation op = add;
    std::cout << op(3, 4) << std::endl;  // 7
}

Callback функции

Практический пример: регистрация обработчика событий.

class Button {
private:
    void (*on_click)() = nullptr;
    
public:
    void set_click_handler(void (*handler)()) {
        on_click = handler;
    }
    
    void click() {
        if (on_click != nullptr) {
            on_click();
        }
    }
};

void handle_click() {
    std::cout << "Button clicked!" << std::endl;
}

int main() {
    Button btn;
    btn.set_click_handler(handle_click);
    btn.click();  // Выведет: Button clicked!
}

Указатели на методы класса

class Calculator {
public:
    int add(int a, int b) { return a + b; }
    int multiply(int a, int b) { return a * b; }
};

int main() {
    // Указатель на метод класса (синтаксис ещё сложнее!)
    int (Calculator::*method_ptr)(int, int) = &Calculator::add;
    
    Calculator calc;
    // Вызов через указатель
    std::cout << (calc.*method_ptr)(3, 4) << std::endl;  // 7
    
    // Через указатель на объект
    Calculator* calc_ptr = &calc;
    std::cout << (calc_ptr->*method_ptr)(3, 4) << std::endl;  // 7
}

Лямбда функции (современный подход)

В С++11 лямбды часто проще, чем указатели на функции:

int main() {
    // Лямбда с автоматическим выводом типа
    auto add = [](int a, int b) { return a + b; };
    
    std::cout << add(3, 4) << std::endl;  // 7
}

Но если нужна именно типизированная переменная, используй std::function:

#include <functional>

std::function<int(int, int)> op = [](int a, int b) { return a + b; };
std::cout << op(3, 4) << std::endl;  // 7

std::function: универсальное решение

Вместо сложных указателей на функции используй std::function:

#include <functional>

using Operation = std::function<int(int, int)>;

int add(int a, int b) { return a + b; }

int main() {
    Operation op1 = add;
    Operation op2 = [](int a, int b) { return a * b; };
    Operation op3 = std::bind(std::plus<int>(), std::placeholders::_1, std::placeholders::_2);
    
    std::cout << op1(3, 4) << std::endl;  // 7
    std::cout << op2(3, 4) << std::endl;  // 12
    std::cout << op3(3, 4) << std::endl;  // 7
}

Сравнение подходов

ПодходСинтаксисГибкостьПроизводительность
Указатель на функциюСложныйСредняяОтличная
typedefСреднийСредняяОтличная
ЛямбдаПростойВысокаяОтличная
std::functionПростойОтличнаяХорошая (небольшие накладные расходы)

Итоговые рекомендации

  • Для простых callback'ов: используй лямбды или std::function
  • Для производительности критичного кода: указатели на функции
  • Для читаемости кода: using вместо typedef
  • Для гибкости: std::function (работает с функциями, лямбдами, функторами)
  • Избегай: сложных синтаксических конструкций с указателями на методы класса
Как объявить указатель на функцию? | PrepBro