연사자의 오버로딩

 

인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다. 
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!

 

 


 

 

목차

     

     


     

    연산자

     


     

    연산자 vs 함수

     

    연산자는 피연산자의 갯수/타입이 고정되어 있다.


     

     연산자 오버로딩

     

    일단 [연산자 함수]를 정의해야 한다.


    함수도 멤버함수 vs 전역함수가 존재하는것처럼, 연산자 함수도 두가지 방식으로 만들 수 있다.

    1. 멤버 연산자 함수 version
    2. 전역 연산자 함수 version

     

     

    더보기

    위의 상황에서는 연산을 할 수 없다. 하지만, 연산자 오버로딩을 하면 Position pos3 = pos + pos2 연산을 할 수 있다. 

     

    #include <iostream>
    using namespace std;
    
    class Position 
    {
    public:
        Position operator+(const Position& arg)
        {
            Position pos;
            pos._x = _x + arg._x;
            pos._y = _y + arg._y;
            return pos;
        }
    
    public:
        int _x;
        int _y;
    };
    
    int main()
    {    
        Position pos;
        pos._x = 0;
        pos._y = 0;
    
        Position pos2;
       pos2._x = 1;
       pos2._y = 1;
    
        Position pos3 = pos + pos2;
        // pos3 = pos.operator+(pos2)   위와 같은 의미
    
        return 0;
    }

     

     


     

     

    멤버 연산자 함수 version

     

    a op b 형태에서 왼쪽으로 기준으로 실행됨 (a가 클래스여야 가능. a를 '기준 피연산자'라고 함)
    한계) a가 클래스가 아니면 사용 못 함.

     

    class Position {
    public:
        Position operator+(const Position& arg)
        {
            Position pos;
            pos._x = _x + arg._x;
            pos._y = _y + arg._y;
            return pos;
        }
    public:
        int _x;
        int _y;
    };
    int main(){    
        //...
    
        Position pos3 = pos + pos2;
        // pos3 = pos.operator+(pos2); //위와 같다
        Position pos4 = pos3 + 1;
        // pos4 = pos3.operator+(1); // 1 + pos3로 연산되니 연산 오류가 난다. 빌드되지 않는다.
    }

    순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다.


     

    전역 연산자 함수 version

     

    a op b 형태라면 a, b 모두를 연산자 함수의 피연산자로 만들어준다.

    전역 연산자 함수 version은 왼쪽이 클래스가 아니어도 문제가 되지 않는다. 

     

    ...
    // 전역 연산자 함수 version
    Position operator+(int a, const Position& b){
        Position ret;
    
        ret._x = b._x + a;
        ret._y = b._y + a;
    
        return ret;
    }
    
    int main(){    
    	//...
        Position pos3 = pos + pos2;
        Position pos4 = pos3 + 1;
        // pos4 = po3.operator+(1)     //위와 같은 의미
        // Position pos4 = 1 + pos3    //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다 
    
        Position pos44 = 1 + pos3;     // 전역 연산자 함수 version으로 사용 가능하다. 
                                      // 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
        return 0;
    }

     


     

     

    멤버 vs. 전역 연산자 함수 


    그럼 무엇이 더 좋은가? 그런거 없음. 심지어 둘 중 하나만 지원하는 경우도 있기 때문

    - 대표적으로 대입 연산자 (a = b)는 전역 연산자 version으로는 못 만든다

     

    //...
    // 전역 연산자 함수 version
    Position operator+(int a, const Position& b){
        Position ret;
    
        ret._x = b._x + a;
        ret._y = b._y + a;
    
        return ret;
    }
    void operator=(int arg){
        _x = arg;
        _y = arg;
    }
    int main(){    
    	//...
        Position pos3 = pos + pos2;
        Position pos4 = 1 + pos3;     // 전역 연산자 함수 version으로 사용 가능하다. 
                                      // 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
    
        bool isSame = (pos3 == pos4);
    
        Position pos5;              // void operator=(int arg)에 대입
        pos5 = 5;					// 대입이 불가능하도록 문법적으로 막아놨다.
    
        return 0;
    }

     

    pos5 = 5의 경우 전역 연산자인 void operator=(int arg)의 대입이 불가능하도록 문법적으로 막아놨다.


     

     

    멤버 vs. 전역 연산자 함수 


    그럼 무엇이 더 좋은가?

    -  그런거 없음. 심지어 둘 중 하나만 지원하는 경우도 있기 때문

    •  대표적으로 대입 연산자 (a = b)는 전역 연산자 version으로는 못 만든다

     

    Position& operator=(int arg)   // 자기자신의 참조값으로 만들어 넘겨줌
    {
        _x = arg;
        _y = arg;
    
        // Position* this = 내자신의 주소
        return *this;
    }
    int main(){
    	...
        Position pos5;        // void operator=(int arg)에 대입
        pos3 = (pos5 = 5);
    
        return 0;
    }

     


     

    전체 코드

     

    더보기
    #include <iostream>
    using namespace std;
    
    class Position 
    {
    public:
        Position operator+(const Position& arg)
        {
            Position pos;
            pos._x = _x + arg._x;
            pos._y = _y + arg._y;
            return pos;
        }
    
        Position operator+(int arg)
        {
            Position pos;
            pos._x = _x + arg;
            pos._y = _y + arg;
            return pos;
        }
    
        bool operator==(const Position& arg)
        {
            return _x == arg._x && _y == arg._y;
        }
    
        void operator=(int arg)
        {
            _x = arg;
            _y = arg;
        }
    
    public:
        int _x;
        int _y;
    };
    
    // 전역 연산자 함수 version
    Position operator+(int a, const Position& b)
    {
        Position ret;
    
        ret._x = b._x + a;
        ret._y = b._y + a;
    
        return ret;
    }
    
    int main()
    {    
        Position pos;
        pos._x = 0;
        pos._y = 0;
    
        Position pos2;
        pos2._x = 1;
        pos2._y = 1;
    
        Position pos3 = pos + pos2;
        // pos3 = pos.operator+(pos2)   //위와 같은 의미
    
        Position pos4 = pos3 + 1;
        // pos4 = po3.operator+(1)     //위와 같은 의미
        // Position pos4 = 1 + pos3    //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다 
    
        Position pos4 = 1 + pos3;     // 전역 연산자 함수 version으로 사용 가능하다. 
                                      // 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
    
        bool isSame = (pos3 == pos4);
    
        Position pos5;              // void operator=(int arg)에 대입
        pos5 = 5;
    
        return 0;
    }

     


     

     

    복사 대입 연산자

     

    -  대입 연산자가 나온 김에 [복사 대입 연산자]에 대해 알아보자
    -  용어가 좀 헷갈린다

    • [복사 생성자] [대입 연산자] [복사 대입 연산자]

    -  복사 대입 연산자 = 대입 연산자 중, 자기 자신의 참조 타입을 인자로 받는 것

     

     

    [복사 생성자] [복사 대입 연산자] 등 특별 대우를 받는 이유는, 객체가 ' 복사 ' 되길 원하는 특징 때문이다.
    TODO ) 동적할당 시간에 더 자세히 알아볼 것

    Position& operator=(const Position& arg)  
    {
        _x = arg._x;
        _y = arg._y; 
    
        // Position* this = 내자신의 주소
        return *this;
    }

     

     

    기타

     

    -  모든 연산자를 다 오버로딩 할 수 있는 것은 아니다 (:: . .* 이런건 안됨)

    -  모든 연산자가 다 2개 항이 있는 것이 아님. ++ -- 가 대표적 (단항 연산자)

    -  증감 연산자 ++ --

    • 전위형 (++a) opertator++()
    • 휘위형 (a++) operator++(int)
    void operator++()
    {
        _x++;
        _y++;
        return *this;
    }
    
    void operator++(int)
    {
        Position ret = *this;
        _x++;
        _y++;
        return ret;
    }

    위의  void형 보다는 자기 자신을 참조(&Position)하는 아래의 버젼이 더 낫다.

    아래와 같이 만들어 ++를 연속적으로 호출할 수 있다.

    Position& operator++()
    {
        _x++;
        _y++;
        return *this;
    }
    
    Position& operator++(int)
    {
        Position ret = *this;
        _x++;
        _y++;
        return ret;
    }

     

     

    연산자

     

     

    int main()
    {
    	Position pos;
        pos._x = 0;
        pos._y = 0;
    
        Position pos2;
        pos2._x = 1;
        pos2._y = 1;
    
        Position pos3 = pos + pos2;
    
        Position pos4 = pos3 + 1;
    
        Position pos4 = 1 + pos3;     // 전역 연산자 함수 version으로 사용 가능하다. 
                                      // 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
        bool isSame = (pos3 == pos4);
    
        Position pos5;              // void operator=(int arg)에 대입
        pos3 = (pos5 = 5);
        // (const Pos&)줘~  (Pos)복사값 줄께~
        
        pos5 = pos3++;
        // 위의 코딩이 연산되는 방식은 아래와 같다
        // Position XXXXXX = pos3++
        // pos5 = XXXXXX;
    
        ++(++pos3);
        // pos3 = pos.operator+(pos2);
    
        return 0;
    }

    call   Position::operator++ (03E12A8h)을 실행하면 스택(Stack) 어딘가에 임시적으로 저장한 후,

    그 복사본의 참조값을 eax에 넣어준다.


     

     

    전체 코드

     

     

    더보기
    #include <iostream>
    using namespace std;
    
    class Position{
    public:
        // 연산자 오버로딩
        Position operator+(const Position& arg)
        {
            Position pos;
            pos._x = _x + arg._x;
            pos._y = _y + arg._y;
            return pos;
        }
    
        Position operator+(int arg)
        {
            Position pos;
            pos._x = _x + arg;
            pos._y = _y + arg;
            return pos;
        }
    
        bool operator==(const Position& arg)
        {
            return _x == arg._x && _y == arg._y;
        }
    
        /*
        Position& operator=(int arg)   // 자기자신의 참조값으로 만들어 넘겨줌
        {
            _x = arg;
            _y = arg;
    
            // Position* this = 내자신의 주소
            return *this;
        }
        */
    
        // [복사 생성자] [복사 대입 연산자] 등 특별 대우를 받는 이유는,
        // 말 그대로 객체가 '복사'되길 원하는 특징 때문이다
        // TODO ) 동적할당 시간에 더 자세히 알아볼 것
        Position& operator=(const Position& arg)  
        {
            _x = arg._x;
            _y = arg._y; 
    
            // Position* this = 내자신의 주소
            return *this;
        }
    
        Position& operator++()
        {
            _x++;
            _y++;
            return *this;
        }
        Position operator++(int)
        {
            Position ret = *this;
            _x++;
            _y++;
            return ret;
        }
    
    public:
        int _x;
        int _y;
    };
    
    // 전역 연산자 함수 version
    Position operator+(int a, const Position& b)
    {
        Position ret;
    
        ret._x = b._x + a;
        ret._y = b._y + a;
    
        return ret;
    }
    
    int main()
    {
        Position pos;
        pos._x = 0;
        pos._y = 0;
    
        Position pos2;
        pos2._x = 1;
        pos2._y = 1;
    
        Position pos3 = pos + pos2;
        // pos3 = pos.operator+(pos2)   //위와 같은 의미
    
        Position pos4 = pos3 + 1;
        // pos4 = po3.operator+(1)     //위와 같은 의미
        // Position pos4 = 1 + pos3    //순서를 바꾸면 멤버 연산자 함수 버젼으로 사용할 수 없다 
    
        Position pos44 = 1 + pos3;     // 전역 연산자 함수 version으로 사용 가능하다. 
                                      // 위에 Position operator+(int a, const Position& b) 전역 연산자 함수 사용.
    
    
        bool isSame = (pos3 == pos4);
    
        Position pos5;              // void operator=(int arg)에 대입
        pos3 = (pos5 = 5);
    
        // (const Pos&)줘~  (Pos)복사값 줄께~
        pos5 = pos3++;
        // 위의 코딩이 연산되는 방식은 아래와 같다
        // Position XXXXXX = pos3++
        // pos5 = XXXXXX;
    
        ++(++pos3);
        // pos3 = pos.operator+(pos2);
    
        return 0;
    }