연산자 오버로딩이란?
=, +, -, * 등과 같은 C++의 표준 연산자를 클래스 객체에 사용할 수 있게 해주는 것이다
즉 하나의 연산자에 다양한 의미를 부여할 수 있다
그렇다면 왜 오버로딩을 하면서 사용해야 할까?
<< 연산자를 생각해보자
<<는 본래 비트 연산자로 쉬프트 기능을 한다
하지만 cout을 통한 출력 명령을 사용할 때도 << 연산자를 사용한다
이런 식으로 코드를 좀 더 자연스럽게 만들어준다
어떻게 오버로딩을 할까?
시간과 분의 값을 가지는 Time이라는 클래스의 객체 work와 break가 있다고 해보자
work와 break의 총 시간을 알고 싶을 때 어떻게 해야할까
보통은 Time 클래스에 void Add(cosnt Time &t)와 같은 멤버 함수를 정의하여 사용할 것이다
하지만 work.Add(break)를 호출하는 것보다 + 연산자를 오버로딩하여 사용하는 쪽이 알아보기도 쉽고 사용이 간편하다
연산자 함수의 형식은 다음과 같다
operator op(argument-list)
op는 오버로딩할 연산자를 나타내는 기호다
위에서 다뤘던 예제를 한번 실전에서 연습해보자
//mytime.h class Time { ... public: ... Time operator+(const Time &t) const; ... } //mytime.cpp ... Time Time::operator+(const Time &t) const { Time sum; sum.minutes = minutes + t.minutes; sum.hours = hours + t.hours + sum.minutes / 60; sum.minutes %= 60; return sum; } ...
+ 연산자를 이용하여 Time 클래스 객체를 더하는 연산자로 오버로딩했다
메인 함수에서 work + break와 같은 명령문을 사용할 수 있다
그렇다면 work1 + break + work2도 가능할까?
이 궁금증은 + 연산자가 어떻게 동작하는지를 보면 알 수 있다
work + break는 다음과 같다
work.operator+(break)
즉 work1 + break + work2는
work1.operator+(break + work2)
와 같고 이는
work1.operator+( break.operator+(work2) )
와 같다
따라서 옳바르게 동작한다
work * 2는 가능한데 2 * work는?
시간을 2배로 늘리는 기능으로 * 연산자를 오버로딩하면 다음과 같다
//mytime.h class Time { ... public: ... Time operator*(const double n) const; ... } //mytime.cpp ... Time Time::operator*(const double n) const { Time result; long totalminutes = hours * n * 60 + minutes * n; result.hours = totalminutes / 60; result.minutes = totalminutes % 60; return result; } ...
work 시간을 2배로 늘리려면 work * 2 명령을 사용하면 된다 (작업 시간이 2배나 늘었다...)
그렇다면 2 * work도 될까?
답은 'No'다
위의 + 연산자는 된다 하지만 * 연산자는 안되는데 왜 안될까?
둘의 멤버 함수 정의를 보면 차이점이 보인다
+ 연산자는 전달 인자로 Time 객체를 참조받고
* 연산자는 전달 인자로 기본 데이터형인 double 데이터형을 참조받는다
이전에 살펴봤던 오버로딩 동작 방식을 다시 한 번 떠올려보면 궁금증이 해결된다
work * 2는
work.operator*(2)
로 문제 없지만 2 * work는
2.operator*(work)
으로 상당히 문제가 있어보인다
우선 operator*는 Time 클래스의 객체의 메세지를 받고 호출된다
하지만 2 * work는 double 데이터형의 메세지를 받고 호출하는 모양이므로 옳지 않다
그리고 전달 인자로 Time 클래스의 객체이므로 함수 정의와 어긋난다
이러한 문제는 오버로딩을 한번 더 하면 해결된다
그럼 위에서 배웠던 것을 참고로 * 연산자를 오버로딩해보자
안타깝지만 위와 같은 방법은 잘못됐다
Time operator*(const double n, const Time &t) const;
* 연산자는 2개의 피연산자를 통해 연산되는 것인데 위 함수 원형은 3개의 피연산자를 인식한다
이를 해결하기 위해서는 멤버가 아닌 함수를 사용하면 되는데 프렌드라는 개념을 알아야 한다
프렌드? friend?
프렌드는 클래스의 멤버 함수들이 가지는 것돠 동등한 접근 권한을 갖게 해준다
즉 프렌드 함수는 멤버 함수는 아니면서도 클래스의 private 멤버에 접근할 수 있다
프렌드를 사용하여 위 문제를 해결해보자
class Time { ... public: ... friend Time operator*(const double n, const Time &t) { return t * n; } ... };
위 함수 원형은 다음 두가지 함축적 의미를 가지고 있다
operator*()함수는 클래스 선언 안에 선언되지만 멤버 함수가 아니기 때문에 멤버 연산자를 사용하여 호출되지 않는다
operator*()함수는 그것이 비록 멤버 함수는 아니지만 멤버 함수와 동등한 접근 권한을 가진다
위에서는 간단하게 이전에 오버로딩한 * 연산자를 사용하게 하는 인라인 함수로 만들었지만
함축적 의미를 확인해볼 수 있는 함수 정의는 다음과 같다
Time operator*(const double n, const Time &t) { Time result; long totalminutes = t.hours * n * 60 + t.minutes * n; result.hours = totalminutes / 60; result.minutes = totalminutes % 60; return result; }
정의에는 friend를 사용하지 않는다
그리고 Time:: 사용 범위 연산자를 사용하지 않는다
마지막으로 전달 받은 객체의 private 멤버를 멤버 연산자 .를 사용하여 직접 접근한다
위 함수는 멤버 함수가 아니기 때문에 Time 클래스 객체가 아니더라도 함수를 사용할 수 있다
즉 2 * work 와 같은 명령문을 사용할 수 있게 된다
여담으로 cout(wcout)에 사용되는 << 연산자를 오버로딩할 때는 ostream(wostream)을 리턴하는 것이 바람직하다
만약 아무것도 리턴하지 않는다면 cout << work << break와 같은 명령문은 오류를 발생시킨다
그 이유는 역시 동작 과정을 알아보면 이해할 수 있다
operator<<(cout, work << break)
operator<<(cout, ?)
리턴 값이 없다면 ? 부분에서 오류가 생기기 때문이다
'Programming Language > C/C++' 카테고리의 다른 글
[C/C++] 난수 생성하기, rand(), srand() (0) | 2017.06.02 |
---|---|
[C/C++] 한글 출력 오류, 유니코드, MBCS, 국제화 (3) | 2017.06.01 |
[C/C++] namespace 제대로 사용하기 (0) | 2017.05.23 |
[C/C++] ios_base 클래스, setf, fmtflags 출력 형식 지정 (0) | 2017.05.18 |
[C/C++] atan2 함수로 각도 구하기 (극 좌표계) (0) | 2017.05.11 |