Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Функтор (Functor) в C++
Определение
Функтор (функциональный объект) — это объект класса, у которого перегружен оператор operator(). Благодаря этому объект можно вызывать как функцию.
Функтор — это объект, который ведёт себя как функция. Это мощный способ создать "функцию" со своим состоянием.
Базовый пример
#include <iostream>
using namespace std;
// Простой функтор
class Adder {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Adder add; // Создаём объект функтора
cout << add(5, 3) << endl; // Вызываем как функцию: 8
// Эквивалентно:
cout << add.operator()(5, 3) << endl; // Явный вызов
return 0;
}
Функтор со состоянием (состояние)
Отличие от обычной функции — функтор может хранить данные:
#include <iostream>
using namespace std;
class Multiplier {
private:
int factor;
public:
Multiplier(int f) : factor(f) {} // Конструктор
int operator()(int x) const {
return x * factor;
}
};
int main() {
Multiplier multiplyBy3(3);
Multiplier multiplyBy10(10);
cout << multiplyBy3(5) << endl; // 5 * 3 = 15
cout << multiplyBy10(5) << endl; // 5 * 10 = 50
return 0;
}
Каждый объект функтора имеет своё состояние — в отличие от функции!
Использование в STL алгоритмах
Фунторы идеальны для передачи в STL функции, ожидающие callback'ов:
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
// Функтор-компаратор для сортировки
class Compare {
private:
bool ascending;
public:
Compare(bool asc = true) : ascending(asc) {}
bool operator()(int a, int b) const {
return ascending ? a < b : a > b; // По возрастанию или убыванию
}
};
int main() {
vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
// Сортировка по возрастанию
sort(nums.begin(), nums.end(), Compare(true));
for (int n : nums) cout << n << " ";
cout << endl; // 1 1 2 3 4 5 6 9
// Сортировка по убыванию
sort(nums.begin(), nums.end(), Compare(false));
for (int n : nums) cout << n << " ";
cout << endl; // 9 6 5 4 3 2 1 1
return 0;
}
Функтор с mutable состоянием
Фунтор может изменять своё состояние:
#include <iostream>
using namespace std;
class Counter {
private:
int count;
public:
Counter() : count(0) {}
int operator()(int x) {
count += x;
return count; // Возвращаем累積сумму
}
int getCount() const { return count; }
};
int main() {
Counter counter;
cout << counter(5) << endl; // 5
cout << counter(3) << endl; // 8
cout << counter(2) << endl; // 10
cout << counter.getCount() << endl; // 10
return 0;
}
Функтор vs Лямбда
С введением лямбд в C++11, функторы используются реже, но они разные инструменты:
#include <algorithm>
#include <vector>
using namespace std;
// Способ 1: Функтор (до C++11)
class IsEven {
public:
bool operator()(int x) const { return x % 2 == 0; }
};
int main() {
vector<int> nums = {1, 2, 3, 4, 5, 6};
// Способ 1: Функтор
auto even1 = find_if(nums.begin(), nums.end(), IsEven());
// Способ 2: Лямбда (C++11, предпочтительнее)
auto even2 = find_if(nums.begin(), nums.end(),
[](int x) { return x % 2 == 0; });
// Способ 3: Обычная функция
auto even3 = find_if(nums.begin(), nums.end(),
[](int x) { return x % 2 == 0; });
return 0;
}
Сравнение:
| Характеристика | Функтор | Лямбда | Функция |
|---|---|---|---|
| Состояние | Может иметь | Может захватить | Нет |
| Синтаксис | Многословный | Компактный | Простой |
| Производительность | Хорошая | Отличная (инлайн) | Хорошая |
| Переиспользование | Хорошее | Плохое | Хорошее |
| Когда использовать | Сложная логика | Простые операции | Публичный API |
std::function и функторы
std::function позволяет хранить любой callable объект (функцию, функтор, лямбду):
#include <functional>
#include <iostream>
using namespace std;
class Printer {
private:
string prefix;
public:
Printer(const string& p) : prefix(p) {}
void operator()(const string& msg) const {
cout << prefix << ": " << msg << endl;
}
};
int main() {
function<void(const string&)> callback;
// Функтор
callback = Printer("INFO");
callback("Application started");
// Output: INFO: Application started
// Лямбда
callback = [](const string& msg) { cout << "LOG: " << msg << endl; };
callback("Processing data");
// Output: LOG: Processing data
return 0;
}
Функторы для трансформаций (transform)
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
class Square {
public:
int operator()(int x) const { return x * x; }
};
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
vector<int> squared(nums.size());
// Применяем функтор к каждому элементу
transform(nums.begin(), nums.end(), squared.begin(), Square());
for (int n : squared)
cout << n << " ";
cout << endl; // 1 4 9 16 25
return 0;
}
Специализированные функторы STL
Стандартная библиотека содержит встроенные функторы:
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> nums = {5, 2, 8, 1, 9};
// less<int> — функтор сравнения
sort(nums.begin(), nums.end(), less<int>()); // По возрастанию
sort(nums.begin(), nums.end(), greater<int>()); // По убыванию
// plus<int>, minus<int>, multiplies<int> и т.д.
int a = 10, b = 3;
cout << plus<int>()(a, b) << endl; // 13
cout << minus<int>()(a, b) << endl; // 7
cout << multiplies<int>()(a, b) << endl; // 30
return 0;
}
Функторы vs Указатели на функции
#include <iostream>
using namespace std;
// Обычная функция
int addFunction(int a, int b) {
return a + b;
}
// Функтор
class AddFunctor {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
// Указатель на функцию
int (*funcPtr)(int, int) = addFunction;
cout << funcPtr(5, 3) << endl; // 8
// Функтор
AddFunctor addObj;
cout << addObj(5, 3) << endl; // 8
// Функтор может быть инлайнирован компилятором,
// указатель на функцию — нет (дополнительный оверхед)!
return 0;
}
Когда использовать функторы
Используйте функторы:
- Для состояния — если нужно хранить данные между вызовами
- Для переиспользования — один функтор, разные параметры через конструктор
- Для производительности — компилятор может инлайнить код
- Для сложной логики — когда лямбда слишком велика
Переходите на лямбды (C++11+):
- Для простых операций
- Для одноразовых функций
- Когда нет нужды в состоянии
Современная C++ практика
C++11+ изменил парадигму:
// Старый способ (до C++11): функторы везде
class Comparator { ... };
sort(v.begin(), v.end(), Comparator());
// Новый способ (C++11+): лямбды предпочтительнее
sort(v.begin(), v.end(), [](int a, int b) { return a < b; });
// Но функторы остаются полезны для сложной логики
class ComplexComparator {
private:
// состояние, параметры и т.д.
};
Итоговые рекомендации
Функтор — это объект класса с перегруженным operator(). Он:
- Может хранить состояние (данные)
- Переиспользуется с разными параметрами конструктора
- Вызывается как функция
- Встроен в STL алгоритмы
- Остаётся мощным инструментом даже в эпоху лямбд
Хотя лямбды (C++11+) вытеснили функторы из повседневного использования, понимание функторов критично для эффективного C++ кода и работы с STL.