본문 바로가기
Programming/열혈 C++ 프로그래밍(저자 윤성우)

Ch 10. 연산자 오버로딩 1

by minjunkim.dev 2020. 8. 17.

    모든 내용은 [윤성우 저, "열혈 C++ 프로그래밍", 오렌지미디어] 를 기반으로 제 나름대로 이해하여 정리한 것입니다. 다소 부정확한 내용이 있을수도 있으니 이를 유념하고 봐주세요!


# 연산자 오버로딩
- 객체도 완벽히 기본 자료형 데이터처럼 취급하게 할 수 있음
- 'operator' 키워드 + '연산자' => 연산자를 이용한 함수의 호출도 허용 
- 연산자 오버로딩한 함수도 const 선언이 가능


# 연산자 오버로딩 방법
1) 멤버함수에 의한 방법

class Point
{
private:
    int xpos,ypos;
public:
    ...
    Point operator+(const Point &ref)
    {	
    	Point pos(xpos+ref.xpos,ypos+ref.ypos);
        return pos;
    }
};


2) 전역함수에 의한 방법 : friend 선언 이용 또는 Get(), Set() 멤버함수 이용 이 동반됨

class Point
{
private:
    int xpos,ypos;
public:
    ...
    freind Point operator+(const Point &pos1, const Point &pos2);
    /*
    이 함수에 대해 friend를 선언하여
    이 함수가 Point 클래스의 private 변수에 직접접근할 수 있게 함
    */
};

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

=> 동일한 자료형을 대상으로 두가지 방법으로 연산자 오버로딩을 할 경우,
멤버함수에 의한 방법이 우선시되어 호출되나, 가급적 이런 경우가 있게 하지는 말자.

# 객체지향에는 전역이라는 개념이 존재하지 않는다.
그러나, C++은 전역에 대한 개념이 여전히 존재하는데(C를 포함하고 있기 때문)
특별한 경우 아니면 쓰지 말자.

# 멤버함수 기반으로만 오버로딩이 가능한 연산자
=(대입연산자), ()(함수호출 연산자), [](배열 접근 연산자), ->(멤버 접근을 위한 포인터 연산자)

 

# 연산자 오버로딩 하는데 있어서의 주의사항

1) 본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다.

2) 연산자의 우선순위와 결합성은 바뀌지 않는다.

3) 매개변수의 디폴트 값 설정이 불가능하다.

4) 연산자의 순수 기능까지 빼앗을 수는 없다.

 

# 증감연산자 오버로딩
++, --

# 전위연산 오버로딩시에는 참조값을 반환하게끔 오버로딩하자.

class Point
{
private:
    int xpos,ypos;
public:
    ...
    Point& operator++()
    {
        xpos+=1
        ypos+=1
        return *this;
    }
    
    friend Point& operator--(Point &ref);
};

Point& operator--(Point &ref)
{
    ref.xpos-=1;
    ref.ypos-=1;
    return ref;
}


# 후위연산 오버로딩임을 표현하고 싶으면 (int)를 추가하고,

후위연산은 const 객체를 반환하게끔 오버로딩하자.

class Point
{
private:
    int xpos,ypos;
public:
    ...
    const Point operator++(int)
    {
        const Point retobj(xpos, ypos); // const Point retobj(*this);
        xpos+=1
        ypos+=1
        return retobj;
    }
    
    friend const Point operator--(Point &ref, int);
};

const Point operator--(Point &ref, int)
{
    const Point retobj(ref);
    ref.xpos-=1;
    ref.ypos-=1;
    return retobj;
}


# 연산자 오버로딩에서의 교환법칙 해결
- 멤버함수 형태로 오버로딩하면, 오버로딩된 멤버함수가 정의된 클래스의 객체가

오버로딩된 연산자의 왼편에 반드시 와야하는 제약이 생긴다.
- 이러한 제약을 해결하기 위해

1) 전역함수의 형태로 오버로딩

2) 멤버함수 + 전역함수 형태를 동시에 이용

class Point
{
private:
    int xpos,ypos;
public:
    Point operator*(int times) // 멤버함수 오버로딩
    {
        Point pos(xpos*times,ypos*times)
        return pos;
    }
    friend Point operator*(int times, Point &ref);
};

Point operator*(int times, Point &ref) // 전역함수 오버로딩 안에서 멤버함수 오버로딩을 이용
{
    return ref*times; 
}


# << 연산자 오버로딩
1) cout은 ostream 클래스의 객체이다.
2) ostream은 이름공간 std 안에 선언되어 있으며,
이의 사용을 위해서는 헤더파일 <iostream>을 포함해야 한다.


# >> 연산자 오버로딩
1) cin은 istream 클래스의 객체이다.
2) istream은 이름공간 std 안에 선언되어 있으며,
이의 사용을 위해서는 헤더파일 <iostream>을 포함해야 한다.

* << 연산자나 >> 연산자 오버로딩에 있어서
1) 멤버함수 오버로딩을 선택한다면 cout, cin 객체의 클래스 안에

멤버함수를 추가하여 오버로딩을 해야하므로 결국 ostream 클래스를 정정해야 한다.
2) 따라서, 전역함수에 의한 방법을 선택하자.


[출처] : 윤성우 저, "열혈 C++ 프로그래밍", 오렌지미디어