Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C++] ( )연산자의 오버로딩과 펑터(Functor) 본문

Programming/C++

[Programming/C++] ( )연산자의 오버로딩과 펑터(Functor)

scii 2021. 2. 5. 21:57

함수 호출에 사용되는 인자의 전달에 사용되는 ( )도 연산자이므로 오버로딩이 가능하다. 그리고 이 연산자를 오버로딩 하면, 객체를 함수처럼 사용할 수 있게 된다.

adder(5, 7);

객체의 이름이 adder이고 이 객체에 ( )연산자가 멤버함수로 오버로딩 되어 있는 상태라면, 다음과 같이 해석된다.

adder.operator()(5, 7);

연산자가 ( )이니 멤버함수의 이름은 operator()이다. 그리고 함수에 전달되는 인자의 정보는 5와 7이다.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


class Point {
private:
    int xpos, ypos;

public:
    explicit Point(const int x = 0, const int y = 0) : xpos(x), ypos(y) {}

    Point operator+(const Point& pos) const {
        return Point(xpos + pos.xpos, ypos + pos.ypos);
    }

    friend std::ostream& operator<<(std::ostream& os, const Point& pos);
};


std::ostream& operator<<(std::ostream& os, const Point& pos) {
    os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
    return os;
}


class Adder {
public:
    int operator()(const int& n1, const int& n2) {
        return n1 + n2;
    }

    double operator()(const double& e1, const double& e2) {
        return e1 + e2;
    }

    Point operator()(const Point& pos1, const Point& pos2) {
        return pos1 + pos2;
    }
};


 int main(const int argc, const char* const argv[]) { 
     Adder adder;
     cout << adder(1, 3) << endl;
     cout << adder(1.5, 3.7) << endl;
     cout << adder(Point(3, 4), Point(5, 5));

     return 0;
}

/* 결과

4
5.2
[8, 9]

*/

위에서 정의한 Adder 클래스와 같이 함수처럼 동작하는 클래스를 가리켜 '펑터(Functor)'라 한다. 그리고 '함수 오브젝트(Function Object)'라고도 불린다.


펑터의 위력

펑터는 함수 또는 객체의 동작방식에 유연함을 제공할 때 주로 사용된다. 

virtual bool operator()(int num1, int num2) const = 0;

위의 순수가상함수를 갖는 클래스는 추상클래스이다. 그리고 operator() 함수가 순수가상함수로 정의되어있다. 따라서 이는 이 함수의 기능을 파생 클래스에서 확정 짓겠다는 의미이다.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


// C#에서의 interface 역할
class SortRule {
public:
    virtual bool operator()(const int num1, const int num2) const = 0;
};


class AscendingSort : public SortRule {
public:
    virtual bool operator()(const int num1, const int num2) const override {
        if (num1 > num2) {
            return true;
        }
        return false;
    }
};


class DescendingSort : public SortRule {
public:
    virtual bool operator()(const int num1, const int num2) const override {
        if (num1 < num2) {
            return true;
        }
        return false;
    }
};


class DataStorage {
private:
    int* arr;
    int idx;
    const int MAX_LEN;

public:
    explicit DataStorage(const int arrlen) : idx(0), MAX_LEN(arrlen) {
        arr = new int[MAX_LEN];
    }

    void AddData(int num) {
        if (MAX_LEN <= idx) {
            cout << "can not save" << endl;
            return;
        }
        arr[idx++] = num;
    }

    void ShowAllData() const {
        for (int i = 0; i < idx; i++) {
            cout << arr[i] << ' ';
        }
        cout << endl;
    }

    // 콜백 함수처럼 객체를 인자로 넘겨서 연산되도록
    void SortData(const SortRule& functor) {
        for (int i = 0; i < (idx - 1); i++) {
            for (int j = 0; j < (idx - 1) - i; j++) {
                if (functor(arr[j], arr[j + 1])) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
};


 int main(const int argc, const char* const argv[]) { 
     DataStorage storage(5);
     storage.AddData(40);
     storage.AddData(30);
     storage.AddData(50);
     storage.AddData(20);
     storage.AddData(10);

     storage.SortData(AscendingSort());
     storage.ShowAllData();

     storage.SortData(DescendingSort());
     storage.ShowAllData();

     return 0;
}

/* 결과

10 20 30 40 50
50 40 30 20 10

*/
void SortData(const SortRule& functor) { ... }

매개변수 형이 SortRule의 참조형이므로, SortRule 클래스를 상속하는 AscendingSort클래스와 DescendingSort클래스의 객체는 인자로 전달 가능하다. 

그리고 SortRule의 operator() 함수는 virtual로 선언되었으니, 파생 클래스의 operator() 함수가 대신 호출된다. 때문에 펑터로 무엇이 전달되느냐에 따라 정렬의 기준이 바뀌게 된다. 이것이 펑터를 정의하는 이유이다.

Comments