@ 16. 1 ~ 17. 1/C++

클래스 메서드와 데이터 멤버에 대한 포인터

namoeye 2015. 11. 18. 19:26

함수포인터는

C++ 에서는

typedef로 선언해서 해당함수의 파라미터와 리턴타입에 호환되는 함수를 이용하면되는데..

typedef bool(*Func)(int, int); //Func 타입을 선언해서 두개의 int 파라미터와 bool타입 리턴값을 가지는 함수 포인터를 표현

Func func1; //선언한뒤에

 

bool test(int num1, int num2) {} //이런식의 함수가 있다면

func1 = test; 또는 포인터이니까 func1 = &test로 하면된다.

C++11에서는 타입 에일리어스 기능이 있어

typedef bool(*Func)(int, int); 이걸

이렇게 using Func = bool(*)(int, int); 이런식으로 표현이 되며

Func func1; //똑같이 사용이 가능하다.

 

어쨌든 지금 보려는건 이게 아니라 클래스의 멤버 변수와 메서드는 어떻게 포인터로 사용할 수 있는지..

클래스의 멤버 또는 메서드의 주소값을 얻어 포인터 변수를 만드는 것은 C++에서는 매우 정상적인 작업이다.

하지만 객체 인스턴스가 없다면 static멤버 static메서드만 포인터로 접근할 수 있다.

왜냐면 기본적으로 멤버와 메서드는 개별 객체에 종속되어 존재하니까..

이 때문에 멤버나 메서드를 포인터를 통해 접근하려면 다음과 같이 반드시 특정 객체의 맥락하에서 역참조 해야만한다.

	

c++ 의 문법중 대부분은 다른 oop 언의 문법과 큰 차이는 없지만, 함수 포인터는 연습이 되지 않으면 바로 써먹기 불편한점이 있다. 

그런데 , 함수 조차 포인터로 호출 할 수 있다는것은 종전의 언어들에서 찾아 볼 수 없는 유연함을 제공 할 것으로 기대한다.


사실 표현 방법이 조금 다를뿐 c# 의 메소드 델리게이트 , as3 의 var a = function(){...} 활용에 해당 하는것으로 파악 된다. 

동적객체에 적용하기 위해 * 연산자를 사용하는데 어순이 조금 이색적이다.






//

//  main.cpp

//  FuntionPointerStudy

//

//  Created by SuperSc on 2015. 1. 7..

//  Copyright (c) 2015 SuperSc. All rights reserved.

//


#include <iostream>

using namespace std;




//정적함수

void StaticFunction(int n)

{

    cout << "static function int : " << n << endl;

}




//네임스페이스 안의 정적 함수

namespace TestNameSpace {

    void InNameSpace(int n)

    {

        cout << "in namesapce static function : " << n << endl;

    }

}




// 클래스

class TestClass

{

    

    

private:

    string _name;

public:

    

    //클래스 안의 정적함수

    static void InClassStaticFuction(int n)

    {

        cout << "in class static function : " << n << endl;

    }

    

    

    TestClass(string name)

    {

        _name = name;

    }

    

    

    //클래스 함수들...

    void PrintInfo() const

    {

        cout << "in class infomation : " << _name << endl;

    }

    

    void InClass1(int n)

    {

        cout << "in class function 1 : " << n << endl;

    }

    

    void InClass2(int n)

    {

        cout << "in class function 2 : " << n << endl;

    }

    

    int Sum(int m , int n)

    {

        int _sum = m + n;

        return _sum;

    }

    

    string GetName() const

    {

        return _name;

    }

    

    

};








int main(int argc, const char * argv[]) {

    // insert code here...

    

    //정적함수 포인터 선언

    void (*staticFunctionPointer)(int);

    staticFunctionPointer = StaticFunction;

    staticFunctionPointer(20);

    

    

    //네임스페이스 안의 정적함수로 교체 (함수 모양이 같으면 치환이 가능하다)

    staticFunctionPointer = TestNameSpace::InNameSpace;

    staticFunctionPointer(30);

    

    

    //클래스의 정적함수로 교체

    staticFunctionPointer = TestClass::InClassStaticFuction;

    staticFunctionPointer(40);

    

    

    

    

    //

    //동적 객체 생성

    //

    TestClass * testObject1 = new TestClass("test_object_1");

    

    

    //활용 : 동적 객체의 -> 활용하여도 클래스의 정적 함수 사용가능

    staticFunctionPointer = testObject1->InClassStaticFuction;

    staticFunctionPointer(50);

    

    

    //에러 ! 동적 객체의 클래스 함수는 모양이 같더라도 정적함수 포인터로 사용 없다.

    //staticFunctionPointer = testObject1->InClass;

    

    

    //선언 : int 인수 1개를 사용하는 동적 객체용 함수 포인터 선언

    void(TestClass::*inClassFunctionPointer)(int);

    

    

    //활용 : 인수 1 함수 포인터 활용

    inClassFunctionPointer = &TestClass::InClass1;

    (testObject1->*inClassFunctionPointer)(60);

    

    

    //활용 : 역시 함수 형태가 같은 같은 동적 객체의 함수도 치환이 가능하다.

    inClassFunctionPointer = &TestClass::InClass2;

    (testObject1->*inClassFunctionPointer)(70);

    

    

    //선언 : 동적 객체의 인수를 사용하지 않는 const 함수 포인터 선언

    void(TestClass::*inClassNoArgFunction)() const;

    

    

    //활용 : 동적 객체의 인수가 없는 함수 포인터 활용

    inClassNoArgFunction = &TestClass::PrintInfo;

    (testObject1->*inClassNoArgFunction)();

    

    

    //선언 : 동적 객체의 인수가 2, 반환값이 int 함수 포인터 선언

    int(TestClass::*inClassDoubleArgFunction)(int,int);

    

    

    //활용 : 동적 객체의 인수 2, 반환값이 int 함수 포인터 활용

    inClassDoubleArgFunction = &TestClass::Sum;

    int result_int = (testObject1->*inClassDoubleArgFunction)(80,90);

    cout<< "result : " << result_int << endl;

    

    

    //선언 : 동적 객체의 반환값이 string 함수 포인터 선언

    string(TestClass::*inClassReturnStringFunction)() const;

    

    

    //활용 : 동적 객체의 반환값이 string 함수 포인터 활용

    inClassReturnStringFunction = &TestClass::GetName;

    string result_string = (testObject1->*inClassReturnStringFunction)();

    cout << "result : " << result_string << endl;

    

    

    

    delete testObject1;

    testObject1 = nullptr;

    

    

    /* 출력내용

     

     static function int : 20

     in namesapce static function : 30

     in class static function : 40

     in class static function : 50

     in class function 1 : 60

     in class function 2 : 70

     in class infomation : test_object_1

     result : 170

     result : test_object_1

     

     */

    

    

    return 0;

}






edit - 15.01.12

c++ 11 부터 아래와 같이 using 을 이용해 간단히 함수 포인터를 선언 할 수 있다.

using EventListener = void(*)(void *  , TEventArg );



EventListener 라는 이름의 함수 포인터는 반환값이 없고 , void * 형과 TEventArg 형의 인자를 가진다.

 클래스 메서드나 데이터 멤버에 대한 포인터를 사용할 일은 많지 않지만,

종종 일반 클래스 메서드를 포인터로서 qsort() 같은 함수에 인자로 넘기는 실수를 할 때가 있다(아래 파란색이 함수포인터니까..). 이때는 의도한 대로 동작하지 않는다..

 

#include <stdlib.h>

 

void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));

 

어쨌든 non-static 맴버 / 메서드에 대해서는 객체 없이 역참조 해서는 안된다는 점은 꼭! 기억!!