본문 바로가기
[Public] 프로그래밍/C++

[C++] STL에서는 왜 함수객체를 사용할까? (함수포인터, 함수객체, 람다)

by 차출발 2012. 2. 8.
반응형
2012. 02. 08

STL와 같이 라이브러리에서 가장 중요한 것은 무엇일가요?

범용성효율성 입니다.
범용성이란 누구나 사용이 용이해야한다.
효율성이란 최적화가 되어야 한다.
이정도로 생각하시면 될듯 싶습니다.

그럼 다음 예제 코드를 봅시다.


그냥 일반적인 덧셈과 뺄셈 함수를 가지는 코드입니다.


여기서 라이브러리를 만든다는 입장을 가지고 접근을 해 봅시다.
아까 이야기 하던 2개의문제 범용성과 효율성가지고 이 두가지를 해결 하면 됩니다.



먼저 효율성의 문제를 해결하기 위해서는 어떻게 해야할가요?

힌트를 들이자면
위 함수에서는 매개변수를 전달하기 위해서는 스택을 사용하게 됩니다.
그러면 오버헤드가 발생할 수 밖에 없습니다.
이 문제를 해결하면 됩니다.

그러기 위해서는 우리가 배웠던 Inline 함수 이것을 이용하면 문제가 해결을 할 수 있습니다.


인라인함수를 사용하면 코드가 치환되게 된다는 사실은 이전 인라인 함수시간에 배웠습니다.
코드가 치환이 되면 호출에 들어가는 오버헤드를 줄일 수가 있습니다.
그러면 우리는 효율성의 문제를 해결하였습니다.




다음으로는 범용성의 문제를 해결하기 위해서는 어떻게 해야할 가요?

네 짐작하셨을거라 생각됩니다.
함수포인터!
매법 덧셈할때는 Add를 뺄셈할 때는 Minus를 호출해야할 것입니다.
하지만 함수포인터를 이용하면 위의 문제를 해결이 가능하게 되겠죠

그래서 우리는 함수포인터를 이용하여 아래와 같이 수정을 하였습니다.


자 그럼 우리는 효율성과 범용성을 모두 해결하였습니다.^^
그럼 우리는 라이브러리를 만들게 되었겠죠?



하지만 위에서 우리가 제시했던 인라인함수와 함수포인터가 제대로 작동할가요?

실질적으로 컴파일은 되지만 인라인함수는 작동하지 않습니다.
함수포인터로 인라인함수를 작동하면 무뉘만 인라인함수고 실제로는 그냥일반함수와 같다는 것입니다.
왜 이런일이 일어날가여?




먼저 알아야 할 게 인라인 함수는 컴파일타임일가요? 런타임일가요?
..
..
네 답은 컴파일 타임입니다. 컴파일을 하는 타임에 결정된다는 것입니다.
함수는 반환형과 매개변수를 지니고 있습니다.
그런데 함수포인터는 반환형과 매개 변수의 종류는 알지만
정확한 타입인지는 모릅니다. 그래서 애매모호한데 컴파일 시키면 안되겠죠
그래서 치환이 안되고 무뉘는 인라인 함수지만 실질적으로는 일반함수로 작동하게 되는것입니다.

대표적인 예로 표준함수의 qsort() 함수가 이런식으로 작동을 합니다.
정렬할 데이터가 많다면 직접 구현한 것이 효율적이라는 것입니다.

그럼 어떻게 해야할가요?
무뉘만 인라인함수이니 효율성문제가 다시 제기되는데 ㅠ0ㅠ

이게 C언어의 한계 입니다.



그럼 어떻게 하라고요 ?????

그래서 우리에게는 C++에서는 다음기능을 제공해주고 있습니다.
C++에서는 새로운 개념함수객체를 제공하는 것입니다.
함수 객체의 간단한 개념은 ()연산자를 재정의하여서 함수처럼 사용하는 것입니다.

함수포인터는 시그니쳐가 같으면 같은 Type이지만
함수객체는 시그니쳐가 같아도 다른 타입이 가능하다는 것이죠
그러면 시그니쳐가 같아도 다른타입인지 구별이 가능하면 인라인함수가 컴파일타임에서 Type을 결정을 지을수 있다는 것입니다.


무슨말인지 모르겠죠? ^^;; 시그니쳐가 어찌고 저찌고
쉽게 말하자면 다음과 같은 정렬함수가 있을때


함수포인터 bool(*cmp)(int, int)를 생성하면 시그니쳐가 같은 함수를 매핑해야 합니다.
즉 함수포인터는 시그니쳐가 같은 cmp_up과 cmp_down 중 둘중 하나를 매핑을 해야하죠

근데 여기서 인라인함수를 적용하면 컴파일타임이기 때문에
어떤함수가 올지 모른다는것입니다. cmp_up  이 올련지 cmp_down이 올련지 예측이 불가능하다는 거죠

컴파일러 왈 : 그래서 어떤 함수를 치환하라고? 에이 몰라 그냥 일반함수로 해

하지만 함
수객체는 같은 시그니쳐여도 구별이 가능하다는 거죠

원래 구조체나 클래스 내부에는 Inline으로 선언되기 때문에 옆의 그림과 같은뜻이 되겠죠
그럼 같은 시그니쳐라도 컴파일러는 예측이 가능하기 때문에
치환이 되는 겁니다.


그럼 다시 정렬함수를 다음과 고치면



그럼 우리는 인라인의 조건을 만족했기 때문에 효율성의 문제를 다시 해결했습니다.
어랏 근데 아직 Down down 보니 범용성을 해결하지 못했네여 ^^;;



그럼 범용성 너는 어떻게 하니 ?

내 어느정도 짐작하신분들은 짐작 하셨을겁니다.
저희 C++ 에서는 템플릿이 있었죠 ㅋ
템플릿을 이용하여 범용성을 해결하는 것입니다.


자 그러면 범용성도 이제 템플릿을 이용하여 해결했네요
와 이제 우린 라이브러리를 생각한 프로그래머가 된것입니다. 짝짝짝

이제 결론을 내릴때가 됬습니다.



우리가 배우는 STL에서 함수객체를 사용하는 이유는 무엇일가요?

이유는 단 하나입니다.
인라인함수를 사용하기 위해서이다. (즉 치환을 하기위해서다)




와우 인라인함수가 그럼 대세군요 모든것을 인라인화 하면 되겠네요 ?

여러분도 알다시피 인라인함수의 문제점이 머라고 했죠? 코드의 길이가 길어진다는
전부다를 인라인화 시킬수가 없습니다.
그럼 어떻게 해요?

그래서 나오는게 새로운 개념인 람다(LAMBDA)라는 개념입니다.
람다는 VS2010과 GCC4.5부터 사용이 가능합니다.



그럼 람다는 어떻게 사용하나요?

위의 예제에서 람다를 사용하는법에대해서 알아보겠습니다.


[]는 람다의 시작을 나타내고
(...) 는 람다식에서 사용할 매개변수를 나타냅니다.
{}는 람다식이 수행할 명령을 기술합니다.

이렇게 되면 알아서 함수객체를 생성하고 인라인을 치환합니다.

결론은 ?
람다라는 새로운 문법을 무작정 쓰지말고
이 문법이 왜 나오게 되었는지 왜 써야하는지 아는게 중요한것입니다.






도움이 되셨다면 리플하나 남겨주세요.
리플 하나가 큰 도움이 된답니다
*^^*