본문 바로가기
개인공부/Rookiss C++ 프로그래밍 입문

Chapter 10 - 콜백 함수

by 하고싶은건많은놈 2023. 3. 29.

함수 포인터 #1

int* pointer = &a

  • int : 데이터 타입
  • pointer : 변수 이름
int Add(int a, int b)
{
    return a + b;
}

int main()
{
    typedef int(FUNC_TYPE)(int a, int b);
    using FUNC_TYPE = int(int a, int b); // modern C++

    FUNC_TYPE* fn = ADD;
    int result = fn(1, 2);

    int result2 = (*fn)(1, 2);
}

FUNC_TYPE* fn = ADD()

  • FUNC_TYPE : 데이터 타입
  • fn : 변수 이름
  • Add : 주소를 저장할 대상 함수

함수의 이름은 함수의 시작 주소를 가짐(배열과 유사)
함수 포인터에는 접근 연산자 *를 붙여도 함수 주소를 가리킴

class Item
{
    public:
        Item() : _itemid(0), _rarity(0), _ownerid(0) {}
    public:
        int _itemid;
        int _rarity;
        int _ownerid;
}

Item* FindItem(Item items[], int itemCount, bool(*selector)(Item* item))
{
    for (int i = 0; i < itemCount; i++)
    {
        Item* item = &items[i];
        if (selector(item))
            return item;
    }

    return nullptr;
}

bool IsRareItem(Item* item)
{
    return item->_rerity >= 2;
}

int main()
{
    Item item[10];
    items[3]._rarity - 2;
    Item* rareItem = FIndItem(items, 10, isRareItem);
}

함수 포인터 #2

typedef는 선언 문법의 앞에 붙이는 형식으로 사용
보통은 int (*PFUNC)(int, int);와 같은 형식으로 함수 포인터 시그니처를 만들어 사용함

class Knight
{
    public:
        int GetHP(int, int)
        {
            ...
        }
    public:
        int _hp = 100;
}

typedef int(Knight::*PMEMFUNC)(int, int);


int main()
{
    Knight k1;
    Knight* k2 = new KNight();
    PMEMFUNC mfn;

    mfn = &Knight::GetHP;
    (k1.*mfn)(1, 2);

    (k2->*mfn)(1, 2); 
    delete k2;

    return 0;
}

멤버 함수는 특정 객체를 기반으로 호출됨
함수 포인터에 함수의 주소를 대입할시 &는 생략이 가능하나, 붙이는 것이 권장됨
특히 멤버 함수를 함수 포인터에 대입할 때는 반드시 &를 붙어야함
멤버 함수를 함수 포인터로 사용시 객체가 필요하며, (k1.*mfn)(1, 2);와 같은 형식으로 사용하여 해당 객체의 멤버가 아닌 함수 포인터라는 것을 지정해야함


함수 객체

함수 포인터 단점

  • 함수 시그니처가 맞지 않으면 사용할 수 없음
  • 상태를 가질 수 없음

함수 객체(Functor) : 함수처럼 동작하는 객체

class Functor
{
    public:
        void operator()()
        {
            cout << "Functor Test" << endl;
            cout << _value << endl;
        }
        boll operator()(int num)
        {
            cout << "Functor Test" << endl;
            _value += num;
            cout << _value << endl;
            return true;
        }
    private:
        int _value = 0;
}

int main()
{
    Functor funcotr;

    functor();
    bool ret = functor(3);

    return 0;
}

함수 객체를 생성하는 시점과 실행하는 시점을 분리할 수 있음

class MoveTask
{
    public:
        void operator()()
        {
            cout << "해당 좌표로 플레이어 이동" << endl;
        }
    public:
        int _playerid;
        int _posX;
        int _posY;
}

int main()
{
    MoveTask task;
    task._plyaerid = 100;
    task._posX = 5;
    task._posY = 0;
    // 이동 요청을 받아서 함수 객체에 저장

    // 요청을 곧바로 실행하지 않고, 얼마든지 나중에 task를 실행할 수 있음
    task();
}

템플릿 기초 #1

template <typename T>
void Print(T a)
{
    cout << a << endl;
}

int main()
{
    print(50);
    print(50.0);
    print("Hello World");
    print<int>(50.0f);

    return 0;
}

템플릿 : 함수나 클래스를 찍어내는 틀

  • 함수 템플릿과 클래스 템플릿이 존재
  • template <typename T> 또는 template <class T1, class T2>의 형식으로 사용
class Knight
{
    public:
        ...
    public:
        int _hp = 100;
};

template <typename T>
void Print(T a)
{
    cout << a << endl;
}

template<<>
void Print(Knight a)
{
    cout << "Knight!!!" << endl;
    cout << a._hp << endl;
}

ostream& operator<<(ostream& os, const Knights& k)
{
    os << k._hol
    return os;
}

int main()
{
    Knight k1;
    Print(k1);
}

Knight 클래스형으로 Print 함수를 사용하고 싶은 경우, Print 함수에서 사용하는 <<연산자에 대해 Knight형의 오버로딩이 필요함
또는 템플릿 특수화를 통해 Print 템플릿 함수를 Knight에 대해서만 재정의할 수 있음


템플릿 기초 #2

template<typename T, iny SIZE>
class RandomBox
{
    public:
        T GetRandomData()
        {
            int idx = rand() % 10;
            return _data[idx];
        }
    public:
        T_data[SIZE];
}

template<int SIZE>
class RandomBox<double, SIZE>
{
    public:
        T GetRandomData()
        {
            cout << "RandomBox Double" << endl;
            int idx = rand() % 10;
            return _data[idx];
        }
    public:
        T_data[SIZE];

}

int main()
{
    srand(static_cast<unsigned int>time(nullptr));

    RandomBox<int, 10> rb1;

    for (int i = 0; i < 10; i++)
        rb1._data[i] = i;
    int value1 = rb1.GetRandomData();
    cout << value1 << endl;

    RandoBox<double, 20> rb2;
    for (int i = 0; i < 20; i++)
        rb2._data[i] = i + 0.5f;
    int value2 = rb2.GetRandomData();
    cout << value2 << endl;
}

클래스 템플릿도 함수 템플릿과 거의 비슷하게 사용
서로 다른 인수를 사용하면 서로 다른 템플릿 클래스가 생성되며, 각각 별개의 클래스로 취급됨
템플릿 특수화도 사용 가능


콜백 함수

콜백(Callback) : 어떤 상황 발생시 특정 기능을 호출

class Item
{
    public:
    public:
        int _itemid = 0;
        int _rarity - 0
        int _owenerid = 0;
};

class FindByOwnerid
{
    public:
        bool operator()(const Item*)
            return (item->_ownerid == _ownerid);
    public:
        int _owenerid;
};

class FindByRarity
{
    public:
        bool operator()(const Item*)
            return (item->_rarity == _rarity);
    public:
        int _rarity;
};

template <typename T>
Item* Finditem(Item items[], int itemCount, T selector)
{
    for (int i = 0; i < itemCount; i++)
    {
        Item* item = &items[i];
        if (selector(item))
            return item;
    }
    return nullptr;
}

int main()
{
    Item items[10];

    FindByOwnerid functor1;
    functor1._ownerid = 100;

    FindByRariry functor2;
    functor2._rarity = 1;

    Item* item1 = Finditem(items, 10, functor1);
    Item* item2 = Finditem(items, 10, functor2);

    return 0;
}

'개인공부 > Rookiss C++ 프로그래밍 입문' 카테고리의 다른 글

Chapter 12 - Modern C++  (0) 2023.03.29
Chapter 11 - STL  (0) 2023.03.29
Chapter 9 - 디버깅  (0) 2023.03.29
Chatper 8 - 실습  (0) 2023.03.29
Chapter 7 - 동적 할당  (0) 2023.03.29

댓글