38-1-다.미리 정의된 함수 객체

함수 객체는 통상 () 연산자 하나만 정의하고 그나마도 동작이 간단해 길이가 아주 짧다. 이런 짧은 클래스도 직접 선언해서 쓰자면 번거로운데 그래서 STL은 자주 사용할만한 연산에 대해 미리 함수 객체를 정의하고 있다. 이런 객체들은 별다른 정의없이 그냥 사용하기만 하면 된다. 대표적으로 가장 간단한 함수 객체인 plus를 보자. 더할 피연산자의 타입 T를 인수로 받아들이는 클래스 템플릿이다.

 

struct plus : public binary_function<T, T, T> {

     T operator()(const T& x, const T& y) const { return (x+y); }

};

 

이 선언문에서 : public 이하의 내용은 다음 항의 주제이며 꼭 없어도 상관없으므로 잠시 무시하도록 하자. 본체 내용도 아주 쉬운데 T형의 x, y를 전달받아 x+y를 리턴한다. T가 아주 뚱뚱한 클래스일 수도 있으므로 값이 아닌 레퍼런스로 전달받고 피연산자를 상수로 취급한다는 정도 외에는 특별할 것도 없다. T 가 int라면 결국 a, b를 받아 a+b를 리턴하는 동작을 하는 함수 객체이다. 간단한 사용예를 보자.

 

: plus

#include <iostream>

#include <functional>

using namespace std;

 

void main()

{

     int a=1,b=2;

     int c=plus<int>()(a,b);

     cout << c << endl;

}

 

함수 객체와 그 지원 매크로, 타입 등은 모두 functional 헤더 파일에 정의되어 있으므로 이 헤더 파일을 인클루드해야 한다. main에서 정수형 변수 a와 b를 선언하고 plus 객체의 함수 ()를 호출하여 두 정수의 합을 계산했다. 여기서 plus<int>() 구문이 조금 복잡해 보이는데 앞에서 설명했다시피 디폴트 생성자 호출문이며 임시 객체를 생성한다. 생성된 임시객체로부터 ()연산자 함수를 호출하되 인수로 a, b를 넘긴 것이다. 좀 쉽게 풀어쓰면 다음 두 줄이 된다.

 

plus<int> P;

int c=P(a,b);

 

plus 클래스 템플릿으로부터 plus<int> 타입의 클래스를 구체화하고 이 클래스 타입의 객체 P를 선언한다. 그리고 P의 오버로딩된 연산자 ()를 호출했는데 이 함수가 두 인수의 합을 리턴하도록 되어 있으므로 결국 c에는 a+b인 3이 대입된다. 객체를 통해 멤버 함수를 호출했을 뿐 별로 희한할 것도 없는 예제이다. plus외에도 많은 함수 객체들이 미리 정의되어 있다.

 

함수 객체

연산

minus

인수의 차를 계산한다.

multiplies

인수의 곱을 계산한다.

divides

인수를 나눈 몫을 리턴한다.

modulus

인수를 나눈 나머지를 리턴한다.

negate

인수 하나를 전달받아 부호를 반대로 만든다.

equal_to

인수가 같은지 비교하여 결과를 bool타입으로 리턴한다.

not_equal_to

인수가 다른지 비교한다.

greater

번째 인수가 번째 인수보다 큰지 조사한다.

less

번째 인수가 번째 인수보다 작은지 조사한다.

greater_equal

번째 인수가 번째 인수보다 크거나 같은지 조사한다.

less_equal

번째 인수가 번째 인수보다 작거나 같은지 조사한다.

logical_and

인수의 논리곱(&&) 결과를 리턴한다.

logical_or

인수의 논리합(||) 결과를 리턴한다.

logical_not

인수 하나를 전달받아 논리부정(!) 리턴한다.

 

헤더 파일을 굳이 열어 보지 않더라도 함수 객체의 이름으로부터 어떤 연산을 하는지 쉽게 유추된다. 이 함수 객체들을 사용하면 알고리즘들의 동작에 여러 가지 다양한 변화를 줄 수 있다. 그 예로 정렬 방식에 변화를 가해 보자. sort 함수는 요소의 < 연산자로 대소를 비교하므로 기본적으로 올림차순으로 정렬하는데 함수 객체를 취하는 다음 버전을 사용하면 정렬 순서를 원하는대로 지정할 수 있다.

 

void sort(RanIt first, RanIt last, BinPred F);

 

마지막 인수 F는 비교할 두 요소를 전달받아 비교 결과를 리턴하는데 함수 객체의 조건을 만족하면 true를 리턴한다. bool형을 리턴하므로 F는 조건자 함수 객체이다. 다음 예제는 문자열을 정렬하는데 일반 sort 함수와 함수 객체 버전으로 각각 정렬한다.

 

: sortdesc

#include <iostream>

#include <string>

#include <vector>

#include <algorithm>

#include <functional>

using namespace std;

 

void main()

{

     string names[]={"STL","MFC","owl","html","pascal","Ada",

          "Delphi","C/C++","Python","basic"};

     vector<string> vs(&names[0],&names[10]);

 

     //sort(vs.begin(),vs.end());

     sort(vs.begin(),vs.end(),greater<string>());

    

     vector<string>::iterator it;

     for (it=vs.begin();it!=vs.end();it++) {

          cout << *it << endl;

     }

}

 

sort의 기본 버전은 요소간의 비교를 위해 < 연산자, 즉 less 비교 함수 객체를 사용하도록 되어 있어 작은 값이 더 앞쪽에 온다. 그러나 greater 함수 객체를 사용하면 큰 값이 더 앞쪽에 오므로 정렬 순서는 반대가 된다.

 

Ada

C/C++

Delphi

MFC

Python

STL

basic

html

owl

pascal

pascal

owl

html

basic

STL

Python

MFC

Delphi

C/C++

Ada

sort로 정렬했을 때

sort(greater)로 정렬했을 때

 

sort는 퀵 정렬 알고리즘대로 비교 및 교환을 수행하는데 비교를 어떤 식으로 하는가에 따라 정렬 순서가 달라진다. 비교가 필요할 때마다 함수 객체를 호출하는데 이 객체가 less인지 greater인지에 따라 오름차순, 내림차순이 결정되는 것이다. 두 함수 객체는 STL 라이브러리에 의해 제공되므로 그냥 쓰기만 하면 된다.

만약 미리 제공되는 함수 객체가 아니라 사용자가 정의한 방식대로 정렬하고 싶다면 직접 함수 객체를 만들어 sort의 세 번째 인수로 전달한다. 다음 예제는 대소문자 구분없이 알파벳순으로 문자열을 오름차순 정렬한다.

 

: sortfunctor

#include <iostream>

#include <string>

#include <vector>

#include <algorithm>

using namespace std;

 

struct compare {

     bool operator()(string a,string b) const {

          return stricmp(a.c_str(),b.c_str()) < 0;

     }

};

 

void main()

{

     string names[]={"STL","MFC","owl","html","pascal","Ada",

          "Delphi","C/C++","Python","basic"};

     vector<string> vs(&names[0],&names[10]);

 

     //sort(vs.begin(),vs.end());

     sort(vs.begin(),vs.end(),compare());

     vector<string>::iterator it;

     for (it=vs.begin();it!=vs.end();it++) {

          cout << *it << endl;

     }

}

 

compare는 인수로 전달된 두 문자열 a, b를 대소문자 구분없이 비교하여 a가 더 작은지를 리턴한다. compare를 쓰지 않는 sort는 string의 < 연산자로만 대소를 비교하므로 대문자가 항상 소문자 앞에 오지만 compare를 사용하는 sort는 대소문자에 상관없이 알파벳순으로 정렬된다.

 

Ada

C/C++

Delphi

MFC

Python

STL

basic

html

owl

pascal

Ada

basic

C/C++

Delphi

html

MFC

owl

pascal

Python

STL

compare를 쓰지 않은 경우

compare를 쓴 경우

 

정렬을 위한 알고리즘 구현은 sort가 하되 비교 방식만 함수 객체로 사용자가 지정할 수 있다. 좀 더 복잡한 객체 컨테이너라면 이차 정렬 조건을 둘 수 있는데 예를 들어 사원들을 이름순으로 정렬하되 혹시 동명이인이 있으면 나이순으로 정렬하도록 세부 정렬 지침을 제공할 수 있다. 비교 구문이 인라인으로 삽입되어 정렬 속도도 굉장히 빠른데 C의 qsort 함수보다도 훨씬 더 빠르다.