[C++] 함수 객체 functor (Function Object)
🔹 Functor란
Functor는 함수처럼 동작하는 객체를 의미합니다. C++에서 함수 포인터를 이용해 함수를 전달할 수 있지만, Functor는 함수 포인터보다 유연성이 높고 객체 지향적인 설계를 할 수 있도록 도와줍니다.
struct와 class 모두 사용가능하고, 아래 예시에서는 간결함을 위해 struct를 사용했습니다.
struct Adder {
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Adder add;
int result = add(3, 4); // result는 7이 됩니다.
return 0;
}
이런식으로 객체에 함수를 정의하여 사용할 수 있는게 functor의 가장 기본입니다.
🔹 Functor을 사용하는 경우
1. STL 알고리즘과 함께 사용
STL 알고리즘(예: std::sort, std::transform, std::find_if 등)은 Functor를 인수로 사용할 수 있습니다. 이를 이용하여, 정렬 기준이나 변환 규칙 등을 다양하게 지정할 수 있습니다. 뒤에 자세한 코드로 알아보겠습니다.
2. 람다 표현식 대체
람다 표현식은 코드를 간결하고 읽기 쉽게 만들 수 있지만, C++11 이전의 버전에서는 사용할 수 없습니다. 이를 대체하기 위해서는 Functor를 사용할 수 있습니다.
3. 함수 객체의 상태 유지
Functor는 객체로서 함수 호출을 지원하므로, 객체 내부의 상태를 유지하면서 함수 호출을 수행할 수 있습니다. 이를 이용하여, 함수 호출 사이에서 상태를 유지하고 활용할 수 있습니다. 뒤에 자세한 코드로 알아보겠습니다.
4. 비교자로 사용
객체를 비교하는 함수나 함수 포인터를 넘겨주어야 하는 경우 Functor를 이용할 수 있습니다. Functor를 이용하면 함수나 함수 포인터와 달리 객체 내부의 상태를 이용할 수 있으므로, 더 다양한 비교 기준을 지정할 수 있습니다.
🔹 STL 알고리즘과 함께 사용
functor은 알고리즘 문제를 풀때 STL과 함께 많이 사용합니다. (사실 피할 수 없음)
대표적으로 sort와 함께 사용하여 정렬 기준을 정하는 예시를 보겠습니다.
#include <algorithm> // std::sort 함수를 사용하기 위해 필요한 헤더 파일
#include <iostream> // std::cout, std::endl 함수를 사용하기 위해 필요한 헤더 파일
#include <vector> // std::vector 컨테이너를 사용하기 위해 필요한 헤더 파일
// Person 구조체 정의
struct Person {
std::string name;
int age;
};
// CompareByName Functor 정의
struct CompareByName {
// Person 구조체를 비교하는 operator() 함수 정의
bool operator()(const Person& a, const Person& b) const {
return a.name < b.name; // 이름(name)을 기준으로 오름차순으로 정렬
}
};
// 메인 함수
int main() {
// Person 구조체를 원소로 하는 벡터 people을 생성하고 초기화
std::vector<Person> people{
{"Alice", 25},
{"Bob", 32},
{"Charlie", 18},
{"David", 45},
{"Eve", 29},
};
// std::sort 함수를 사용하여 people 벡터를 CompareByName Functor를 이용하여 정렬
std::sort(people.begin(), people.end(), CompareByName{});
// 정렬된 people 벡터를 출력
for (const auto& p : people) {
std::cout << p.name << ", " << p.age << std::endl;
}
return 0;
}
🔹 함수 객체의 상태 유지
#include <iostream>
class Incrementer {
public:
Incrementer(int initialValue) : value(initialValue) {}
int operator()() {
return value++;
}
private:
int value;
};
int main() {
Incrementer incrementer(0);
std::cout << incrementer() << std::endl; // 0
std::cout << incrementer() << std::endl; // 1
std::cout << incrementer() << std::endl; // 2
return 0;
}
위 코드에서는 Incrementer 클래스가 함수 객체로 사용됩니다. 이 클래스는 생성자에서 초기값을 받아들이고, operator() 함수를 구현하여 값을 1씩 증가시키며 반환합니다.
main 함수에서는 Incrementer 객체를 생성하고, 이를 함수처럼 호출합니다. 이때 Incrementer 객체는 자신의 내부 상태를 유지하면서 값을 증가시키므로, 0부터 차례대로 1씩 증가한 값을 출력합니다.
이렇게 Functor를 사용하면, 객체 내부의 상태를 유지하면서 함수 호출을 수행할 수 있습니다. 이를 이용하면, 함수 호출 사이에서 상태를 유지하고 활용할 수 있습니다.
'Programming > 알고리즘' 카테고리의 다른 글
[C++] Template 이용하기 (Generic 프로그래밍) (0) | 2023.04.24 |
---|---|
[C++] class와 struct 차이. 무엇을 언제 써야할까? (0) | 2023.04.24 |
[알고리즘] 부분 수열 (w/ 비트 마스크) - 파이썬 (0) | 2021.08.22 |
[알고리즘] 다양한 정렬 (버블, 카운팅, 선택 정렬) - 파이썬 (0) | 2021.08.22 |
[알고리즘] 시간복잡도 & 공간복잡도 (0) | 2021.08.20 |