ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [c++] 연산자 오버로딩 (operator overloading) (1)
    C++ 2023. 1. 8. 18:03

    말그대로 연산자를 오버로딩하는 것이다.

     

    기본자료형만 가지고 할 수 있는

    덧셈,뺄셈, 곱셈, 나눗셈, 대입연산을

    객체도 할 수 있게 만들 수 있다.

     

    객체 + 객체 라는 코드가 있으면

    이때 객체 간의 연산이므로

    (정의되어 있는게 아니므로!)

    어떻게 연산작업이 수행될지 직접 정의해주어야함

     

    따라서 해당 객체 클래스 안에 호출될 연산자함수를 정의해주는 것임

     

    형식은 operator+() 꼴.

     

    예를 들어 보자면,

     

    Point 클래스의 p1 + p2 라는 코드가 있으면,

    p1.operator+(Point& ref) 라고 정의된 함수가 호출되는 식이다!

     

    class Point {
    private:
        int xpos, ypos;
    public:
        Point(int x = 0, int y = 0) : xpos(x), ypos(y) {
            // empty
        }
    
        // 덧셈연산
        Point& operator+(const Point& ref) {
            Point pos(xpos + ref.xpos, ypos + ref.ypos);
            return pos;
        }
    
        // 전위증가
        const Point& operator++() {
            xpos++;
            ypos++;
            return *this;
        }
    
        // 후위증가
        const Point& operator++(int) {
            const Point temp(xpos, ypos);
            xpos++;
            ypos++;
            return temp;
        }
    };

     


    [연산자 오버로딩의 return 형이 클래스 타입인 이유(void 가 아닌 이유)]

     

    여기서 오버로딩 함수의 반환형으로

    왜 Point& 즉, 참조형을 쓰는지 궁금할 수 있다.

     

     void operator++() {
    	xpos++;
    	ypos++;
    }

     

    그냥 이렇게 void 형으로 써도 되는데 말이다 

     

    참조형으로 반환하는 이유는

    연속적으로 연산자를 적용할때를 대비해서 그런거다

     

    예를들어

    가장 보편적으로 쓰는 경우

     

    Point a(1,0);
    Point b(3,4);
    Point c;
    
    c = a + b;

     

    이 경우에 a + b 를 진행한 후에 

    c에 대입할 객체가 반환되어야한다

     

    따라서 return형이 void 형이 아닌 클래스type인 것이다

     

     

    특별한 경우를 예시로 들면,

    ++(++pos);
    
    ++(pos.operator++());

     

    위와 같은 경우 ++ 전위증가를 두 번 연속으로 하는데, 이때 만약 void 형이면 두번째 ++ 을 적용할 수 없다!

    따라서 연속으로 연산을 진행하려면 꼭 리턴값이 참조값 반환형으로 정의되어야한다!

     

    그래서 비교연산 ( > < == ) 빼고는 다 해당 객체 타입으로 리턴한다고 보면 된다

    비교연산은 bool 로 리턴한다.

    즉 return type은 bool 아님 객체타입 이 두 가지라고 생각하면 된다

     


    [연산자 오버로딩 함수 선언 방법]

     

    보통 왼편 오른편 둘 다 똑같은 클래스 객체가 오는 상황밖에 없을때는 지역함수만 구현해놔도 ㄱㅊ은데

    만약 서로 다른 두 클래스 간 연산을 오버로딩할때는

    교환법칙의 성립을 위해 전역함수 형태로도 오버라이딩 하는 수 밖에 없다.

    이유는 아래

     

    Point a,b,c;
    
    // 내 객체가 왼쪽
    c = a+b;
    
    // 내 객체가 오른쪽
    c = 3*a;

     

    오버로딩 선언 시 추가적으로 고려해야할 것은 객체가 왼편에 있는지 오른편에 있는지이다.

     

    // pos1+pos2는
    
    pos1.operator+(pos2)
    
    operator+(pos1,pos2)
    
    //이렇게 두 가지로 해석이 가능한데

     

    객체가 항상 왼편에 있는 경우면 그냥 클래스 내에 연산자 오버로딩함수를 구현하면 되는데

     

    객체가 오른편에 올 상황도 있거나 오른편에 와도 정상적으로 작동하게 하려면

    전역+friend 조합을 써야한다.

     

    클래스 내 정의한 것은 묵시적으로 왼쪽 객체가 호출하는 상황에만 적용된다!

     

     

    이게 왼쪽에 있으면

    c = a + b;
    
    c = a.operator+(b);

     

    이렇게 변형되어 실행이 되는데

    c = 3 * a;
    
    // 이런 경우는
    
    3.operator*(a)
    
    // 이런게 안통한다

     

    따라서 이런 경우는 우리가 전역함수로 오버로딩함수를 만들어놓고 해당 클래스 내에 friend로 선언해놓는다.

     

    이걸 전역함수로 만들면

     

    Point operator*(int n,Point p){
    	Point retp;
    	retp.x = p.x*n;
    	retp.y = p.y*n;
    	return retp;
    }

     

    이렇게 되는데 이것의 문제점은

     

    해당 함수에서 해당 객체의 x, y 변수에 접근할 수 없다는 것이다

    왜냐하면 해당 x,y변수들은 Point 클래스의 private 변수이기 때문이다

     

    따라서 해당 전역함수가 클래스 내 private 변수에 접근할 수 있게끔

    Point 클래스 안에서 friend로 선언부만 작성해놓는 것이다!

     


    [반환형에서의 const 선언과 const 객체의 구별]

     

    위의 후위증가 연산자 오버로딩이다.

    const Point& operator++(int) {
        const Point temp(xpos, ypos);
        xpos++;
        ypos++;
        return temp;
    }

     

    여기서 함수 선언부에 const가 앞에 있으니 이건 반환형이 const 라는 뜻이다!

    ( const 함수이려면 const가 맨 뒤에 있어야함 )

     

    따라서 반환하는 값이 Point 참조형이고, 이 참조자를 통해 반환할때 생기는 임시객체를 상수화시켜 내보낸다.

     

    cf)

    const 객체는 const 함수만 호출가능하다!

    const 객체를 대상으로 참조자를 선언할때는 참조자도 const로 선언해야 한다.