끝나지 않는 프로그래밍 일기


1. 함수 객체(Function Object)

이번에는 함수 객체(Function Object)에 대해 알아보도록 하겠습니다. 함수 포인터에 이어 함수 객체는 어떠한 기능일까요? 쉽게 말하자면, 함수 객체는 객체가 함수처럼 동작한다 하여 함수 객체라고 할 수 있는데 우리가 전에 배웠던 연산자 오버로딩에서 ()라는 연산자를 오버로딩하여 객체를 함수처럼 쓸 수 있습니다. 우선은 함수 객체를 알아보기 전에, 간단히 () 연산자에 대해 짚고 넘어가도록 하겠습니다.


우리가 살펴볼 () 연산자는 함수 호출 연산자라고 불리며, 위에서 말했듯이 이 연산자를 오버로딩하게 되면 그 객체는 함수 객체라고 할 수 있습니다. 함수 호출 연산자를 한번 정의하여, 함수 호출 연산자가 도대체 어떤 녀석인지를 봐보도록 할까요? 아래의 예제는 함수 호출 연산자를 오버로딩하는 예제입니다.

#include <iostream>
using namespace std;

class Plus
{
public:
	int operator()(int a, int b)
	{
		return a + b;
	}
};

int main()
{
	Plus pls;

	cout << "pls(10, 20): " << pls(10, 20) << endl;
	return 0;
}

결과:

pls(10, 20): 30

위의 예제를 보시면, Plus 클래스에서 함수 호출 연산자를 오버로딩하여 정수형 데이터 두 개를 받을 수 있고, 받은 두 정수형 데이터를 서로 더해 반환하는 연산자가 정의되어 있습니다. 그리고 메인 함수 내에서는 객체 pls를 함수를 호출하듯, 매개변수를 넘기고 서로 더한 값을 받아 출력하고 있습니다. 아직 연산자 오버로딩을 기억하시고 계시다면, 'pls(10, 20)'과 같은 코드는 'pls.operator()(10, 20)'에서 '.operator()'가 생략된 형태임을 알고 계실겁니다.


이번에는 두개의 정수형 데이터를 받고, 두 정수형 데이터가 서로 같으면 1을 다르면 0을 반환하게끔 코드를 작성하도록 해봅시다.

#include <iostream>
using namespace std;

class Equal
{
public:
	int operator()(int a, int b)
	{
		return a == b;
	}
};

int main()
{
	Equal cmp;

	cout << "pls(5, 5): " << cmp(5, 5) << endl;
	cout << "cmp.operator()(10, 20): " << cmp.operator()(10, 20) << endl;
	cout << "Equal()(10, 10): " << Equal()(10, 10) << endl;
	return 0;
}

결과:

pls(5, 5): 1
cmp.operator()(10, 20): 0
Equal()(10, 10): 1

위 예제에서 주목하셔야 할 부분은 함수 호출 연산자가 오버로딩 되었다는것도 있지만, 메인 함수 내에 Equal 객체로 어떻게 함수가 호출되고 있는지에 대해 보셔야 합니다. 첫번째 호출인 'cmp(5, 5)'는 우리가 첫 예제에서 보아왔던 코드로, 암묵적 호출이라고 합니다. 여기서 암묵적 호출은 밖으로 드러내지 않는 호출이라고 할 수 있으며, 그 아래에 있는 두번째 호출 'cmp.operator()(10, 20)'은 명시적 호출이라고 할 수 있습니다. 분명하게 드러내는 호출이라고 할 수 있는거죠. 그리고 세번째 호출인 'Equal()(10, 20)'은 임시 객체를 통한 암묵적 호출이라고 할 수 있습니다.


여기서 드는 의문점은, 왜 굳이 함수 호출 연산자를 오버로딩하여 객체를 가지고 함수처럼 사용할까요? 우리가 사용하고 있는 함수와는 달리 함수 객체를 사용하면 속성을 지닐 수 있는것이 가능하고, 함수 객체가 일반적인 함수보다 빠르기도 하며, 각각의 함수객체는 서로 다른 타입을 지닌다는 장점이 존재합니다. 아래의 예제는 함수 객체의 속성을 통해 값을 누적시켜, 총합을 출력하는 예제입니다.

#include <iostream>
using namespace std;

class MoneyBox
{
	int total;
public:
	MoneyBox(int _init = 0) : total(_init) { }
	int operator()(int money)
	{
		total += money;
		return total;
	}
};

int main()
{
	MoneyBox mb;

	cout << "mb(100): " << mb(100) << endl;
	cout << "mb(500): " << mb(500) << endl;
	cout << "mb(2000): " << mb(2000) << endl;
	return 0;
}

결과:

mb(100): 100
mb(500): 600
mb(2000): 2600

함수 객체에 정수형 데이터를 넘겨주면 그 값을 받아 누적시키고 반환하게 되며, 동작을 포현할 수 있으나 상태를 저장할 수 없는 일반 함수와는 상당히 비교가 되죠? 함수 객체를 사용하면 일반 함수보다 더 폭넓게, 유연하게 사용할 수 있습니다. 오늘은 함수 객체에 대해서 여기까지 살펴보도록 하겠고, 읽어주신 분들 모두 수고하셨습니다.