개념 및 기초
- 함수의 시작 번지를 가르키는 포인터 (어셈블리 언어의 호출 스택을 생각해보자)
- 포인터 형식 자체가 다르다.
- 리턴타입(*함수 포인터 이름)(인수목록)
- 함수 : int Func(int a) => 포인터 : int(*funcPtr)(int)
- 함수의 시작번지에 들어가기 앞서 인수들의 목록과 리턴 형식(주소) 정보를 (스택에) 저장해야하므로
함수 포인터 사용 방법
- 대입 & 호출
- 대입 : funcPtr = Func; (괄호없이 단독으로 사용된 함수명은 함수의 시작 번지를 나타내는 포인터 상수.)
- 호출 : 원칙적으로 (*funcPtr)(2); 하지만 컴파일러는 funcPtr(2); 도 동작하게한다.
- 인자가 잘못 들어가거나 리턴형식을 잘못된 타입으로 받으면 당연히 컴파일 에러, 함수 포인터 형식이 복잡한 이유
- 하나의 고유한 타입인 함수 포인터.
- 원형이 다른 함수 포인터끼리 직접 대입, 복사 불가.
int(*funcPtr1)(int), double(*funcPtr2)(float); funcPtr1 = funcPtr2; //에러
- 캐스팅 가능
int(*funcPtr1)(int), double(*funcPtr2)(float); funcPtr1 = (int(*)(int))funcPtr2;
- 응용
- 함수 포인터 배열
int(*funcPtrArr[MAX_FUNC_NUM])(int); //함수 포인터 변수명 다음에 배열 크기 선언 funcPtrArr[0] = funcPtr1;
- 함수 포인터의 포인터
int(**funcPPtr)(int); //일단 이건데 불편, 위에도 마찬가지 typedef int(*FUNCPTR)(int); //함수 포인터 타입을 typedef로 정의하고(정의 형식 주의) FUNCPTR* funcPPtr; //이렇게나 FUNCPTR funcPtr[MAX_FUNC_NUM]; //이렇게 사용하는 것이 가독성이 높다.
함수 포인터의 의미
- 함수를 변수로 사용할 수 있다.
- 하나의 함수포인터만을 가지고 조건에 따라 다른 함수를 실행시킬 수 있다.
(fsm에서 활용하는 방법) - 함수를 인자로 넘길 수 있다.
- 함수 포인터 배열이나 구조체를 사용하여 전체 함수그룹을 변경하는 것도 가능하다.
- 하나의 함수포인터만을 가지고 조건에 따라 다른 함수를 실행시킬 수 있다.
- 사용하기 좋은 경우
- 조건에 따라 선택해야 할 함수가 두 개 이상인 경우. 함수 포인터 배열을 활용하자
- 함수 선택 분기와 실제 호출 시점이 다른 경우. 미리 선택한뒤 변수에 저장된 함수를 바로 호출하면 된다.
- 호출할 함수가 외부 모듈(DLL)에 있는 경우, 동적 연결하려면 함수 포인터 사용해야한다.
함수 인자 사용하기
- 예제 : 퀵소트
- 퀵소트 형식
void qsort(void *base, size_t num, size_t width, int ( *compare )(const void *, const void *));
- compare 함수
int compare(const void *a, const void *b) { if (*(int *)a == *(int *)b) return 0; if (*(int *)a > *(int *)b) return 1; return -1; }
- 실제 퀵소트 호출
int ar[]={34,25,27,19,4,127,9,629,18,7,9,165}; qsort(ar,sizeof(ar)/sizeof(ar[0]),sizeof(int),compare); for (int i=0;i<sizeof(ar)/sizeof(ar[0]);i++) { printf("%d번째 = %d\n",i,ar[i]); }
- 퀵소트 형식
클래스 멤버함수의 함수 포인터
- 클래스 내부에 있는 메소드, 멤버함수들을 함수 포인터로 사용하고 싶을때.
- 기존 C의 함수들은 정적(Static)함수로, 소속이나 사용 인스턴스와 무관하게 사용되었다.
- 하지만 클래스의 멤버함수들은 Class 이름을 네임스페이스로 (Class::Function();)
인스턴스에 의존적으로(this->Function();) 사용되므로 차이가 있다. - 예전 스터디 static부분참조
실제 코드로 보는 일반/멤버함수 포인터의 차이점과 사용방법 예시
class World { public: void Close() {} static void Open() {} }; void foo(){} int main() { //대입 void(*fPtr1)() = &foo; //일반 함수 포인터 void(*fPtr2)() = &World::Open; //클래스 멤버 함수 포인터. //클래스의 네임스페이스 설정이 안되어 있으나 static이므로 접근 가능 void(*fPtr3)() = &World::Close; //함수 포인터 소속 불명. void(World::*fPtr4)() = &World::Close; //함수의 소속을 명시하면 OK //호출 fPtr4(); //하지만 인스턴스에 바인딩 되지 않으면 에러 World earth; earth.fPtr4(); //earth 내부의 멤버함수 fptr4를 찾기때문에 불가능 (earth.*fPtr4)(); //원칙적 사용방법 이렇게 쓰도록 한다. }
결론
- 멤버함수를 클래스 외부에서 함수 포인터로 선언(사용)할 때 타입에 클래스 소속을 명시해서 써야한다.
- 멤버함수 호출할때 인스턴스를 참조해서 호출해야한다.
std::function
- 일반 함수 포인터의 문제
- 반환값이 명시적으로 (암시적 형변환 불가) 같은 타입이 아니면 컴파일 에러
- 오로지 함수만 호환이 가능하다. (functor, 멤버함수 포인터, 람다함수, bind반환값 등 안됨)
- std::function을 사용하는 것의 장점
- 함수 반환값이 암시적 형변환 가능한 타입이면 대입 가능하다.
- static 함수 포인터 말고도 functor, 멤버함수 포인터, 람다함수, bind반환값 등 위아더월
- 유연한 사용이 가능하다. (특히 인자로 함수를 받는 경우. 다양한 활용가능)
std::bind
일련의 인자들을 함수에 바인딩시킨 함수 객체를 반환하는 함수 템플릿
- std::bind(함수 포인터, 인자...);
예시
void show_text(const string& t) { cout << "TEXT: " << t << endl; } // show_text 함수에 "Bound function"이라는 문자열을 바인딩시킨 // function<void ()> 타입을 반환한다. function <void ()> f = std::bind(show_text, "Bound function");
- std::bind(함수 포인터, 인자...);
std::placeHolder를 활용하여 추가 인수 bind
- bind의 인자 대신 std::placeHolders::_1, _2, _3... _N을 입력
예시
int multiply(int a, int b) { return a * b; } int main() { // function f 는 multiply 함수에 a 인자는 5로 고정시키고, b 인자는 placeholding만 한다. // 이럼으로써, function f는 하나의 인자(x)를 취하지만, // 그것이 결국 multiply(5, x)의 형태로 함수를 호출하는 것이다. auto f = bind(multiply, 5, _1); for (int i = 0; i < 10; i++) { cout << "5 * " << i << " = " << f(i) << endl; } return 0; }
클래스 멤버함수의 경우 this를 바인딩해서 쉽게 사용가능
- reference_wrapper를 사용하여 인스턴스를 참조로 넘겨야 복사가 발생하지 않는다고 한다.(잘 모르겠음)
예시
BindTest bt; auto f2 = bind(&BindTest::MemberFunc, std::ref(bt), _1); f2(10); // bt.MemberFunc(10);
함수 객체 (functor)
- 객체를 함수처럼 사용하는 것
- 객체의 식별자를 함수 이름처럼 : ()연산자를 오버로딩
- algorithm함수에서 많이 사용된다.
- 함수 포인터와 차이점
- 멤버 변수, 멤버 함수를 가질 수 있다.
- template 클래스로 범용적으로 사용할 수 있다.
'컴퓨터 > C++' 카테고리의 다른 글
FSM과 State 패턴 (0) | 2015.03.10 |
---|---|
c++의 여러가지 생성자들 (0) | 2015.03.10 |
c++의 static 변수에 대하여 (0) | 2015.03.10 |
c++의 가상함수 (0) | 2015.03.10 |