C, C++/C++ 언어 / / 2024. 5. 16.

[C++] 클래스의 멤버 함수 포인터 (member function pointer)

멤버 함수 포인터와 함수 포인터의 차이점

클래스의 멤버 함수 포인터( 이하, 멤버 함수 포인터 )와 일반 함수 포인터는 몇 가지 차이점이 있습니다.

 

일반 함수 포인터에 관한 자세한 내용은 여기에 링크해 두었습니다.

 

[C++] 함수 포인터 개념 및 사용하는 이유

함수 포인터란 무엇인가?이전 글에서 포인터를 이렇게 설명했습니다. 포인터란 객체의 메모리 주소를 저장하는 변수입니다.여기서 객체란 변수, 배열, 구조체, 클래스, 함수 등 메모리에

codingembers.co.kr

 

첫 번째, 멤버 함수 포인터는 클래스 내의 함수를 가리키는 것임을 명시해야 합니다.

다음 예제를 보겠습니다.

#include <iostream>
using namespace std;

class CSomething{
    int m_nValue;

public:

    CSomething(int val) : m_nValue(val){}

    int add(int a, int b){		// 멤버 함수
        return m_nValue + a + b;	// 클래스 멤버 m_nValue에 접근
    }
};

int add(int a, int b){	// 일반 함수 
    return a + b;
}

int main(){

    CSomething cObj(10);
    
    // 멤버 함수 포인터
    int (CSomething::*pFunc)(int, int) = CSomething::add;

    cout << (cObj.*pFunc)(2, 3) << endl;	// 멤버 함수 포인터 사용법

    return 0;
}

멤버 함수 add와 일반 함수 add의 구별을 할 수 없기 때문에, 멤버 함수를 가리키기 위한 클래스 명영역(scope) 연산자 ::을 사용해서 어떤 함수를 지시할 것인지를 분명히 해야 합니다.

 

그래서, 멤버 함수 포인터의 선언은 다음과 같습니다.

함수_반환_타입 (클래스::*포인터 명)(인자, 인자...);

멤버 함수 포인터는 가리키고자 하는 함수의 반환 타입, 인자의 타입을 그대로 따라야 합니다.

여기서 쓰인 *는 연산자가 아니고 포인터임을 표시하기 위한 기호입니다.

 

초기화할 때도 어떤 함수인지 명시해야 합니다.

int (CSomething::*pFunc)(int, int) = CSomething::add;

 

두 번째, 멤버 함수 포인터는 함수를 포함하는 객체를 명시해야 합니다.

그래서 일반 함수 포인터 사용법과 멤버 함수 포인터 사용법이 다릅니다.

여기서 사용한 CSomething 클래스는 위의 예제와 같습니다.

int main(){

    int (*pFunc1)(int, int) = add;  // 일반 함수 포인터
    cout << pFunc1(2, 3) << endl;   // 5
    
    CSomething cObj1(10);
    CSomething cObj2(20);
    
    // 멤버 함수 포인터
    int (CSomething::*pFunc2)(int, int) = CSomething::add;

    cout << (cObj1.*pFunc2)(2, 3) << endl;   // 15
    cout << (cObj2.*pFunc2)(2, 3) << endl;   // 25

    return 0;
}

멤버 함수 포인터를 사용하려면, 어떤 객체의 함수를 사용할 것인지 명시해야 합니다.

cObj1 객체는 m_nValue = 10의 값을 가졌고, cObj2 객체는 20의 값을 가졌기 때문에, 같은 멤버 함수 포인터를 사용하더라도 결과 값이 달라지게 됩니다.

 

그리고, 일반 함수 포인터는 역참조 연산자를 사용한 (*pFunc1)(2,3) 대신 pFunc1(2,3)을 사용할 수도 있습니다.

그러나, 멤버 함수 포인터는 반드시 역참조 연산자를 사용해야 합니다.

역참조 연산자를 사용하지 않으면 컴파일러는 pFunc2를 객체의 함수로 오해해서, 존재하지 않는 함수를 사용한 것에 대한 오류를 발생시키기 때문입니다.

 

그런데, 이 멤버 함수 포인터를 클래스 내에서 사용하려면 어떻게 해야 될까요?

위에선 객체를 명시해야 포인터를 통해서 그 객체의 멤버 함수에 접근할 수 있다고 했는데, 어떻게 컴파일러에게 객체를 알려줄 수 있을까요?

class CSomething{
    int m_nValue;
    
public:

    CSomething(int val) : m_nValue(val){}

    // 멤버 함수
    int add(int a, int b){
        return m_nValue + a + b;
    }

    int add2(int a, int b){
        
        // 멤버 함수 포인터
        // 클래스 내의 함수이므로 add 앞에 something:: 을 붙일 필요 없습니다.
        int (CSomething::*pFunc)(int, int) = add;
        
        // this 객체를 명시
        return (this->*pFunc)(a,b);
    }
};

그때는 자기 자신을 가리키는 this 포인터를 사용하면 됩니다.

일반적으로 this 포인터는 생략을 하더라도 문제가 되지 않지만, 여기에선 반드시 사용해서 자기 자신을 가리키는 객체의 함수를 호출하고 있다는 것을 명시해야 합니다.

참고로, this는 포인터이므로 ->연산자를 사용합니다.

 

정적 멤버 함수 포인터

정적(static) 멤버 함수 포인터는 클래스 내에 위치한 함수를 가리킨다는 점만 제외하면 일반 함수 포인터와 같습니다.

그래서, 초기화할 때 클래스 명을 명시하는 것을 제외하고 사용법도 똑같습니다.

#include <iostream>
using namespace std;

class CSomething{
    
public:

    // 정적 멤버 함수
    static int add(int a, int b){

    	cout << "static function called\n";
        return a + b;
    }
};

int add(int a, int b){  // 일반 함수
    return a + b;
}

int main(){

    int (*pFunc1)(int, int) = add;  // 일반 함수 포인터
    cout << pFunc1(2, 3) << endl;   // 5
    
    // 정적 멤버 함수 포인터
    int (*pFunc2)(int, int) = CSomething::add;

    cout << pFunc2(2, 3) << endl;   // 5 

    return 0;
}

 

이 글과 관련된 글들

함수 포인터를 확장한 std::function 사용법

 

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유