그럼 제일 먼저 함수 객체 가 무엇 인지 알 아 보 죠 . 이 글 을 읽 으시 는 독자 대부분 이 함수 객체 가 무엇 인지 를 대강 아실 듯 하지만 . .. 그래도 기본 을 다시 짚 어 본다는 의미 에서 설명 해 보 도록 하 겠 습니다 . ' 함수 객체 란 무엇 인가 ? ' 그 이름 에서 뜻 을 유추 해 본다면 함수 처럼 작동 하 는 객체 다 라고 말씀 드릴 수 있 겠 네요 . 그럼 일단 은 객체 라고 했으니 , class 나 struct 로 정의 를 해야 하 는 것 같 은데 . .. 함수 처럼 작동 하 게 하 려면 어떻게 하 면 될까요 ? 이 질문 에 답 을 할 수 있 으려면 ' 함수 처럼 작동 한다 ' 라는 것 이 어떤 것 을 뜻 하 는 것 인지 를 짚 어 봐야 할 것 입니다 . C ++ 에서 " 함수 ! " 하 면 가장 먼저 어떤 것 이 떠오르 시 나요 ? 함수 를 정의 하 는 것 과 함수 를 호출 하 는 것 이 두 가지 떠오르 실 겁니다 . ( 아니 면 말 구요 ^_^)// 다음 은 함수 정의 void foo ( ) { statements ;}// 다음 은 함수 호출 / / C ++ 에서 는 어떤 식별자 에 '()' 가 따라오 면 함수 호출 로 인식 합니다 . foo ( ) ; 그럼 다시 질문 들어갑니다 . 함수 처럼 작동 하 게 하 려면 어떻게 하 면 될까요 ? ( 자 ! 잠깐 글 에서 눈 을 떼 시 고 한 번 생각 해 보 세요 ~)...... 제 가 힌트 하나 드릴까요 ? C ++ 에서 '+', '-', '*', '<<' 등 대부분 의 연산자 를 재정 의 하 실 수 있 는 거 기억 하 시 나요 ? 그걸 이용 하 시 면 됩니다 . ...... 그래도 잘 모르 시 겠 다구요 ? 그럼 하나 더 힌트 를 드리 죠 . 이거 가르쳐 드리 면 거의 답 을 가르쳐 드리 는 건데 . .. C ++ 에서 '()' 가 함수 호출 연산자 라는 거 아 시 나요 ? ...... 아하 ! 그렇 죠 . 클래스 정의 안 에서 함수 호출 연산자 '()' 를 재정 의 하 시 면 되 는 겁니다 . 이해 가 안 되 신다면 이론 을 백 번 설명 하 는 것 보다 간단 한 예 가 훨씬 낫 죠 . 자 ~ 예 를 한 번 들 어 볼까요 ? 다음 은 합계 와 평균 을 구해 주 는 함수 객체 입니다 . class Statistics { private : int count ; int sum ; double average ; public : Statistics ( ) : count ( 0 ) , sum ( 0 ) , average ( 0 . 0 ) {} / / '()' 연산자 를 재정 의 . 이게 핵심 입니다 . int operator ( ) ( int i ) { sum += i ; count ++; return sum ; } int N ( ) { return count ; } int SUM ( ) { return sum ; } double AVG ( ) { average = static _ cast < double >( sum ) / count ; return average ; }}; 이 함수 객체 를 사용 할 때 는 다음 과 같이 할 수 있 습니다 . intmain ( void ) { Statistics s ; for ( int i = 1 ; i <= 100 ; i ++) { s ( i ) ; / / 함수 객체 호출 . 그냥 함수 호출 과 구분 이 안 되 죠 ? } cout << " sum = " << s . SUM ( ) << ", avg = " << s . AVG ( ) << endl ; return 0 ;} 어때요 ? 함수 객체 가 함수 보다 좋 다는 감 이 오 시 나요 ? 전 젤 처음 에 함수 객체 라는 개념 을 봤 을 때 " 요거 물건 이 네 . .." 라는 감 이 오 더군요 . 제 가 생각 하 기 에 함수 객체 의 가장 큰 장점 은 함수 는 함수 인데 상태 를 가질 수 있 는 함수 이 다 라는 것 이 더군요 . 위 에 보 시 는 Statistics 의 예 처럼 상태 를 가질 수 있 다는 특성 때문 에 보통 함수 보다 는 할 수 있 는 일 이 훨씬 다양 해 지 게 되 거든요 . 거기 에다가 함수 가 쓰일 수 있 는 곳 이 라면 어디 에 든 쓰일 수 가 있 게 되 는 것 이 죠 . 또 다른 예 로 , 함수 객체 를 써서 구구단 을 한 번 찍 어 볼까요 ? 먼저 인자 로 주어진 수 에 생 성시 에 정해진 수 를 곱하 는 함수 객체 를 작성 해 보 겠 습니다 . class PrintTimes { private : int _ n ; public : PrintTimes ( int n ) : _ n ( n ) {} void operator ( ) ( int i ) { cout << _ n << " * " << i << " = " << i * _ n << endl ; }}; 이 함수 를 써서 구구단 을 출력 하 려면 다음 과 같이 하 면 되 겠 죠 ? intmain ( void ) { vector < int > v ; for ( int i = 1 ; i < 10 ; i ++) { v . push _ back ( i ) ; } for ( vector < int >:: iterator ii = ++ v . begin ( ) ; / / 2 단 부터 출력 ii ! = v . end ( ) ; ++ ii ) { cout << "<< " << * ii << " 단 >>" << endl ; / / PrintTimes ( * ii ) 가 호출 되 면서 _ n 이 차례 로 2 ~ 9 로 초기 화 됩니다 . / / 그리고 for _ each ( ) 알고리즘 을 사용 합니다 . for _ each ( v . begin ( ) , v . end ( ) , PrintTimes ( * ii ) ) ; } return 0 ;} 위 코드 에서 설명 한 대로 PrintTimes ( * ii ) 와 같이 호출 하 면 PrintTimes 함수 객체 의 임시 객체 가 생성 되 면서 _ n 멤버 변수 가 각 단계 별 로 2 ~ 9 로 설정 됩니다 . 그러면 , 구구단 의 2 단 부터 9 단 까지 를 for _ each ( ) 알고리즘 을 사용 해서 출력 할 수 있 게 되 는 것 이 지요 . 이런 식 으로 함수 객체 는 실행 시 에 그 행동 방식 을 바꿀 수 가 있 게 됩니다 . 이번 엔 다른 식 으로 이 문제 를 해결 해 볼까요 ? 이번 엔 PrintTimes 함수 객체 를 둘 로 나누 어 m x n = mn 으로 찍 어 주 는 함수 객체 와 m 을 고정 시켜 주 는 객체 로 나누 어 보 도록 하 겠 습니다 . 다음 과 같이 말 이 죠 . // m x n = mn 을 출력 하 는 함수 객체 struct PrintMultiplication { void operator ( ) ( int m , int n ) { cout << m << " * " << n << " = " << m * n << endl ; }};// m 을 고정 시켜 주 는 객체 class Binder { public : / / 호출 할 함수 객체 와 고정 할 인자 를 기억 해 놓 습니다 Binder ( const PrintMultiplication & op , int arg ) : _ op ( op ) , _ arg ( arg ) {} / / 첫 번 째 인자 를 고정 된 값 으로 호출 합니다 void operator ( ) ( int i ) { _ op ( _ arg , i ) ; } private : PrintMultiplication _ op ; / / 호출 할 함수 객체 int _ arg ; / / 고정 할 인자 의 값 }; intmain ( void ) { vector < int > v ; for ( int i = 1 ; i < 10 ; i ++) { v . push _ back ( i ) ; } for ( vector < int >:: iterator ii = ++ v . begin ( ) ; / / 2 단 부터 출력 ii ! = v . end ( ) ; ++ ii ) { cout << "<< " << * ii << " 단 >>" << endl ; / / Binder ( ) 가 호출 되 면서 PrintMultiplication ( ) 함수 객체 의 첫 번 째 / / 인자 가 * ii 로 for _ each ( ) 알고리즘 이 수행 되 는 동안 고정 됩니다 for _ each ( v . begin ( ) , v . end ( ) , Binder ( PrintMultiplication ( ) , * ii ) ; } return 0 ;} 이해 가 되 시 나요 ? 위 코드 만 보 면 왜 괜히 PrintMultiplication 과 Binder 를 굳이 나누 었 을까 가 이해 되 지 않 겠 지만 Binder 를 템플 릿 화 시키 면 그 장점 이 확연히 드러나 게 됩니다 . Binder 를 템플 릿 화 시키 면 다음 과 같이 작성 할 수 있 을 것 입니다 . template < class OP , typename ARG > class Binder { public : / / 호출 할 함수 객체 와 고정 할 인자 를 기억 해 놓 습니다 Binder ( const OP & op , ARG arg ) : _ op ( op ) , _ arg ( arg ) {} / / 첫 번 째 인자 를 고정 된 값 으로 호출 합니다 void operator ( ) ( int i ) { _ op ( _ arg , i ) ; } private : OP _ op ; / / 호출 할 함수 객체 ARG _ arg ; / / 고정 할 인자 의 값 } 위 와 같이 작성 하 면 ARG 타입 의 인자 를 갖 는 임의 의 OP 타입 함수 객체 에 대해 인자 binding 이 가능 하 게 됩니다 . 사실 C ++ 표준 라이브러리 에 정의 된 bind 1 st , bind 2 nd 의 배경 이 되 는 아이디어 도 위 코드 에서 정의 한 Binder 클래스 와 정확히 일치 합니다 . 게다가 C ++ 표준 라이브러리 에서 는 기본 적 인 연산 에 대해서 이미 함수 객체 로 정의 해 놓 고 있 습니다 . 다음 은 미리 정의 된 함수 객체 들 입니다 . 표현식 효과 negate < type >()- 인자 plus < type >() 인자 1 + 인자 2 minus < type >() 인자 1 – 인자 2 multiplies < type >() 인자 1 * 인자 2 divides < type >() 인자 1 / 인자 2 modules < type >() 인자 1 % 인자 2 equal _ to < type >() 인자 1 == 인자 2 not _ equal _ to < type >() 인자 1 ! = 인자 2 less < type >() 인자 1 < 인자 2 greater < type >() 인자 1 > 인자 2 less _ equal < type >() 인자 1 <= 인자 2 greater _ equal < type >() 인자 1 >= 인자 2 logical _ not < type >()! 인자 logical _ and < type >() 인자 1 && 인자 2 logical _ or < type >() 인자 1 || 인자 bind 1 st ( op , value ) op ( value , 인자 ) bind 2 nd ( op , value ) op ( 인자 , value ) not 1 ( op ) ! op ( 인자 ) not 2 ( op ) ! op ( 인자 1 , 인자 2 ) 이러 한 함수 객체 들 을 사용 하 면 다양 한 작업 을 손쉽 게 할 수 있 습니다 . 예 를 들 어 , vector 에 있 는 모든 수 에 3 씩 더 하 고 싶 다 그런 경우 에 는 transform ( ) 알고리즘 을 사용 해서 다음 과 같이 할 수 있 습니다 . / / 아래 의 Print 템플 릿 함수 는 다음 과 같이 정의 되 어 있 습니다 . template < typename T > struct Print { void operator ( ) ( T v ) { cout << v << " "; }}; for _ each ( v . begin ( ) , v . end ( ) , Print < int >()); cout << "\ n "; transform ( v . begin ( ) , v . end ( ) , v . begin ( ) , bind 2 nd ( plus < int >(), 3 ) ) ; for _ each ( v . begin ( ) , v . end ( ) , Print < int >()); cout << "\ n "; plus < int >() 함수 객체 의 두 번 째 인자 를 3 으로 고정 하 여 원 하 는 결과 를 얻 어 낼 수 있 습니다 . 다음 은 위 코드 를 실행 한 결과 입니다 . 1 2 3 4 5 6 7 8 94 5 6 7 8 9 10 11 12 마지막 으로 한 가지 만 더 예 를 들 고 이번 글 을 마칠까 합니다 . pos = find _ if ( v . begin ( ) , v . end ( ) , not 1 ( bind 2 nd ( modulus < int >(), 2 ) ) ) ; 위 코드 에서 bind 2 nd ( modulus < int >(), 2 ) 는 2 로 나눈 나머지 를 리턴 하 게 되 므로 홀수 에 대해서 는 1 을 리턴 하 게 될 것 입니다 . 그리고 , 그 결과 에 not 1 이 수행 되 므로 짝수 가 참 이 되 어 결국 위 코드 는 첫 번 째 짝수 위치 를 찾 게 될 것 입니다 . 마지막 으로 다음 두 가지 를 기억 해 두 시 면 좋 을 것 같 습니다 . find _ if ( ) 알고리즘 에 대한 것 은 Alones 님 의 std : : find _ if 사용 해 보 기 를 참고 하 세요 1 . 함수 객체 는 함수 처럼 동작 하 는 객체 이 다 . 2 . 함수 객체 는 '()' 연산자 를 재정 의 해서 정의 할 수 있 다 . 역시 나 이번 에 도 생각 보다 글 이 많이 길 어 졌 습니다 . 항상 시작 할 때 는 간단 하 게 써야 겠 다고 다짐 을 하 는데도 , 제 성격 상 어쩔 수 없이 길 어 지나 봅니다 . 너무 길 어 지 면 지루 할테 니 이 쯤 에서 이번 글 을 마무리 하 고 다음 에 도 함수 객체 에 대해 못다한 이야기 를 이 어 가 렵니다 . 혹시 틀린 부분 이나 보완 하 고 싶 으시 부분 이 있 으면 언제 라도 알려 주 시 고 , 소프트웨어 관련 된 저 의 다른 글 들 도 참고 로 읽 어 보 세요 .