코가손의 블로그
C++ 콜백 함수 본문
콜백 함수란, 상황이 일어나길 기다리다가 조건이 맞춰지면 특정한 기능을 호출하도록 하는 함수를 말한다.
콜백 함수를 이해하기위해 먼저 함수 포인터와 함수 객체에 대해 먼저 알아보겠다.
함수 포인터
말 그대로 함수를 가리키는 포인터 이다.
함수의 로직 자체를 매개변수로 넘겨주고 싶을 때 사용한다.
// int(int a, int b) -> Add함수의 시그니처 라고 말함
int Add(int a, int b)
{
return a + b;
}
// 정의1
// typedef int(FUNC_TYPE)(int a, int b); C++ 11 이전
// using FUNC_TYPE = int(int a, int b); Modern C++
// FUNC_TYPE* fn;
// 정의2
int (*fn)(int, int);
// 함수 포인터
fn = &Add; // &생략 가능(C 언어 호환성 때문)
int result1 = fn(1, 2);
int result2 = (*fn)(1, 2); // 함수포인터는 *(접근 연산자)붙여도 함수 주소를 뜻함
함수 포인터 응용
class Item
{
public:
int _itemId; // 아이템을 구분하기 위함
int _rarity; // 희귀도
int _ownerId; // 소지자ID
}
Item* FindItem(Item items[], int itemCount, ITEM_SELECTOR* selector)
{
for(int i = 0; i < itemCount; i++)
{
Item* item = &items[i];
// TODO : 동작-> ID별 탐색, 희귀도별 탐색, 소지자 별 탐색, ...etc
// 함수 포인터 이용하지 않으면 각 로직마다 함수를 따로 만들어야 함
if (selector(item))
return item;
}
return nullptr;
}
bool IsRareItem(Item* item)
{
return item->_rarity >= 2;
}
// 함수 포인터
using ITEM_SELECTOR = bool(Item* item);
ITEM_SELECTOR *selector;
selector = &IsRareItem;
...
FindItem(items[], count, selector);
하지만, 이 방법으로는 전역함수/정적함수 와 같이 호출규약이 동일한 함수만 포인터에 담을 수 있다.
클래스의 멤버함수를 호출하려면 아래와 같은 방법을 사용해야 한다.
typedef int(*PFUNC)(int, int);
typedef int(Knight::*PMEMFUNC)(int, int);
Knight k1;
k1.GetHp(1, 1);
PMEMFUNC mfn;
mfn = &Knight::GetHp;
(k1.*mfn)(1, 1);
Knight* k2 = new Knight();
((*k2).*mfn)(1, 1);
(k2->*mfn)(1, 1);
함수 객체
함수처럼 동작하는 객체를 말한다.
// 함수 포인터 이용
void HelloWorld()
{
cout << "Hello World" << endl;
}
int main()
{
void (*pfunc)(void);
pfunc = &HelloWorld;
// 함수 호출
(*pfunc)();
}
함수 포인터는 함수 포인터 객체의 상태를 표현할 수 없고 시그니처를 맞춰 주어야 사용가능하다는 단점이 있다.
이러한 단점을 보완한 함수 객체는 어떤지 살펴보겠다.
// 함수 객체 이용
// HelloWorld() 의 ()연산자 오버로딩 필요하다
class Functor
{
public:
void operator()()
{
cout<<"Functor Test"<<endl;
cout<< _value <<endl;
}
bool operator()(int num)
{
return true;
}
private:
int _value = 0;
}
int main()
{
Functor functor;
functor();
bool ret = functor(1);
}
함수 객체를 이용하면
함수 객체의 상태를 미리 정의해두고(이동 좌표, 플레이어 상태 등)
정의해둔 상태를 실행하는 함수를 발동하여 (상태에 따른 좌표이동)
여유가 될 때, 또는 실행해야 될 때를 나중에 미루어서 동작시킬 수 있다.
이것을 Command Pattern이라고 한다.
콜백 함수
특정한 상황이 일어났을 때, 기능을 호출하는 콜백 함수를
함수 객체, 템플릿을 이용하여 구현해 보았다.
class Item
{
public:
int _itemId = 0;
int _rarity = 0;
int _ownerId = 0;
};
// 함수 객체
class FindByOwnerId
{
public:
bool operator()(const Item* item)
{
return (item->_ownerId == _ownerId);
}
public:
int _ownerId; // 함수 객체 상태
};
// 함수 객체2
class FindByRarity
{
public:
bool operator()(const Item* item)
{
return (item->_rarity == _rarity);
}
public:
int _rarity;
};
template<typename T>
Item* FindItem(Item items[], int itemCount, T selector)
{
for (int i = 0; i < itemCount; i++)
{
Item* item = &items[i];
if (selector(item))
return item;
}
return nullptr;
}
int main()
{
Item items[10];
items[3]._ownerId = 100;
items[8]._rarity = 2;
FindByOwnerId functor1;
functor1._ownerId = 100;
FindByRarity functor2;
functor2._rarity = 2;
Item* item1 = FindItem(items, 10, functor1);
Item* item2 = FindItem(items, 10, functor2);
return 0;
}
'C++ > 문법' 카테고리의 다른 글
[C++/기초] using vs typedef (0) | 2021.11.11 |
---|---|
C++ 중괄호 초기화 {} (0) | 2021.11.11 |
C++ 캐스팅(static_cast, dynamic_cast, const_cast, reinterpret_cast) (0) | 2021.11.11 |
C++ 문자와 문자열 (0) | 2021.11.11 |
C++ 동적 할당 (0) | 2021.11.11 |
Comments