function object (tsz. function objects)
operator()
túlterhelésével érhető el.
Egy funktor egy olyan osztálypéldány, amely az operator()
túlterhelésével definiálja a függvényszerű viselkedést. Ez lehetővé teszi, hogy az objektumot függvényként hívjuk meg.
Példa egy egyszerű funktorra:
#include <iostream>
// Funktor osztály
class Osszegzo {
public:
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Osszegzo osszeg;
std::cout << "5 + 3 = " << osszeg(5, 3) << std::endl; // 5 + 3 = 8
return 0;
}
Mi történik itt?
- Az Osszegzo
osztály definiál egy operator()()
függvényt.
- Az osszeg
nevű példány úgy hívható meg, mintha függvény lenne: osszeg(5, 3)
.
- A funktor így egy paraméterezhető, függvényszerű viselkedést biztosító objektum.
A funktoroknak több előnyük van a hagyományos függvényekhez képest:
operator()
túlterhelésével bonyolultabb működést lehet beépíteni.std::sort
, std::for_each
.
A funktorok képesek állapotot tárolni, ami azt jelenti, hogy az objektum memóriában megőrzi az előző állapotát.
#include <iostream>
class Szorzo {
private:
int faktor;
public:
Szorzo(int f) : faktor(f) {}
int operator()(int x) {
return x * faktor;
}
};
int main() {
Szorzo duplazo(2);
Szorzo haromszorozo(3);
std::cout << "5 * 2 = " << duplazo(5) << std::endl; // 10
std::cout << "5 * 3 = " << haromszorozo(5) << std::endl; // 15
return 0;
}
Itt:
- A Szorzo
osztály egy számot (faktor
) tárol.
- A operator()
minden híváskor megszorozza a bemenetet ezzel az értékkel.
Ez a fajta állapotmegőrzés nem érhető el normál függvényekkel.
A funktorokat gyakran használják az STL algoritmusokkal. Például az std::sort
-tal együtt:
#include <iostream>
#include <vector>
#include <algorithm>
// Csökkenő rendezést végző funktor
class CsokkenoRendezes {
public:
bool operator()(int a, int b) {
return a > b; // Csökkenő sorrend
}
};
int main() {
std::vector<int> szamok = {3, 1, 4, 1, 5, 9};
std::sort(szamok.begin(), szamok.end(), CsokkenoRendezes());
for (int szam : szamok) {
std::cout << szam << " ";
}
return 0;
}
Magyarázat:
- Az std::sort
normálisan növekvő sorrendben rendezi az elemeket.
- A CsokkenoRendezes
funktorral megfordíthatjuk a sorrendet.
A C++ STL (Standard Template Library) számos beépített funktort tartalmaz az #include <functional>
fejléccel.
Példa az std::greater<int>
használatára csökkenő rendezéshez:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // Szükséges a beépített funktorokhoz
int main() {
std::vector<int> szamok = {3, 1, 4, 1, 5, 9};
std::sort(szamok.begin(), szamok.end(), std::greater<int>());
for (int szam : szamok) {
std::cout << szam << " ";
}
return 0;
}
Ez ugyanazt éri el, mint a korábbi CsokkenoRendezes
funktor, de nem kell saját osztályt írnunk.
Gyakori beépített funktorok:
| Funktor | Leírás | |———|——–| | std::plus<T>
| Összeadás | | std::minus<T>
| Kivonás | | std::multiplies<T>
| Szorzás | | std::divides<T>
| Osztás | | std::modulus<T>
| Maradékos osztás | | std::negate<T>
| Negáció | | std::greater<T>
| Nagyobb összehasonlítás | | std::less<T>
| Kisebb összehasonlítás | | std::equal_to<T>
| Egyenlőségvizsgálat | | std::not_equal_to<T>
| Nem egyenlőségvizsgálat |
A C++11 bevezetett egy modernebb alternatívát: lambda kifejezések.
Példa funktor helyett lambdával:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> szamok = {3, 1, 4, 1, 5, 9};
std::sort(szamok.begin(), szamok.end(), (int a, int b) {
return a > b;
});
for (int szam : szamok) {
std::cout << szam << " ";
}
return 0;
}
Ez a lambda kifejezés egyszerűbb és rövidebb, mint egy külön funktor osztály létrehozása.
Mikor érdemes funktort használni a lambdák helyett?
- Ha az állapotot meg kell őrizni az objektumban.
- Ha a kód újrafelhasználhatósága fontos.
- Ha a függvény nagyon komplex és többször kell használni.
operator()()
-t, így függvényszerűen hívható.std::greater
, std::plus
stb.).