증가/감소 연산자 오버로딩 |
- p.operator++ ( ) //멤버
- operator ++(p) //전역
- #include <iostream>
- using namespace std;
- class Point {
- private:
- int x, y;
- public:
- Point(int _x=0, int _y=0):x(_x), y(_y){}
- void ShowPosition();
- Point& operator++(); //멤버 함수
- friend Point& operator--(Point& p);
- };
- void Point::ShowPosition(){
- cout<<x<<" "<<y<<endl;
- }
- Point& Point::operator++() //멤버함수 정의
- {
- x++;
- y++;
- return *this;
- }
- Point& operator--(Point& p) //전역함수
- {
- p.x--;
- p.y--;
- return p;
- }
- int main(void)
- {
- Point p(1, 2);
- ++p;
- p.ShowPosition();
- --p;
- p.ShowPosition();
- ++(++p);
- p.ShowPosition();
- --(--p);
- p.ShowPosition();
- return 0;
- }
- void main()
- {
- int n;
- ++(++n);
- }
따라서 ++(++p) 연산에서도 괄호 안의 연산인 p.operator++() 함수 호출이 끝나고 나서 이 자리에 p가 그대로 리턴이
돼야 이런 증가 연산이 제대로 이루어 질 수 있다.
- Point& Point::operator++() //멤버함수 정의
- {
- x++;
- y++;
- return *this;
- }
멤버 함수 정의부를 보면 return 타입이 *this인 것을 알 수 있다. 여기서의 this는 Point 객체의 포인터를
의미하는데, *를 붙였으니까 Point객체 자신을 의미한다. 그럼 나 자신을 무엇으로 리턴 하느냐? 바로 Point& 즉, 참조로 리턴을
하는 것이다.
괄호 안은 p.operatr++()이 되고 이 연산이 끝나고 p의 참조를 리턴하는데 p의 참조는 p와 같다. ++(p참조)
-> p참조 . operator++() 이 것이 된다. p참조는 p자신 이므로, p.operator++() 이 되는 것과 같다. 그래서
결과가 ' 3 4 '로 연산이 제대로 되는 것이다.
만약 참조로 리턴 안하고 Point로 리턴 한다면 어떻게 될까?
- #include <iostream>
- using namespace std;
- class Point {
- private:
- int x, y;
- public:
- Point(int _x=0, int _y=0):x(_x), y(_y){}
- void ShowPosition();
- Point operator++(); //멤버 함수
- friend Point operator--(Point& p);
- };
- void Point::ShowPosition(){
- cout<<x<<" "<<y<<endl;
- }
- Point Point::operator++() //멤버함수 정의
- {
- x++;
- y++;
- return *this;
- }
- Point operator--(Point& p) //전역함수
- {
- p.x--;
- p.y--;
- return p;
- }
- int main(void)
- {
- Point p(1, 2);
- ++p;
- p.ShowPosition();
- --p;
- p.ShowPosition();
- ++(++p);
- p.ShowPosition();
- --(--p);
- p.ShowPosition();
- return 0;
- }
그냥 Point를 리턴 한다면, 나 자신을 복사 해서 리턴 하는것이다. 그래서 ++(++p) 에서 괄호 안의 연산후에 이 자리에 오는
것은 p가 아니라 p객체의 복사본이 리턴되는것이다. 그리고 p객체의 복사본을 가지고 ++연산을 하게 되는것이다. 결과적으로 리턴하는 순간에
새로운 객체를 만들어서 리턴하는 꼴이 되므로 p객체는 단지 한번만 증가 하게 되는 것이다. (이것이 문제가 된다)
선연산과 후연산의 구분 |
- ++ p : 변수의 값을 먼저 증가 시킨후 연산
- p++ : 연산후 값 증가
우리가 단항 연산자 오버로딩을 설계할 때, 이처럼 문법상 기본 자료형이 하는 일을 따라 간다면 우리는 선연산 후연산이 다르게 동작
되어야 하는것을 알 수 있는 것이다. 그래서 우리는 호출 되는 함수의 구분을 위해 C++은 또 하나의 약속을 해야 하는 것이다. 다음과 같이
말이다.
- ++p 은 p.operator ++( );
- p++ 은 p.operator ++(Data Type);
여기서 선언된 데이터 타입은(int, double 등등의...) ++ 연산을 구분 지어 주기 위해서만 의미를 가진다. 예제 소스를
보자.
- #include <iostream>
- using namespace std;
- class Point {
- private:
- int x, y;
- public:
- Point(int _x=0, int _y=0):x(_x), y(_y){}
- void ShowPosition();
- Point& operator++(); //++p
- Point operator++(int); // p++
- };
- void Point::ShowPosition(){
- cout<<x<<" "<<y<<endl;
- }
- Point& Point::operator++(){
- x++;
- y++;
- return *this;
- }
- Point Point::operator++(int){
- Point temp(x, y); // Point temp(*this);
- x++;
- y++;
- return temp;
- }
- int main(void)
- {
- Point p1(1, 2);
- (p1++).ShowPosition();
- p1.ShowPosition();
- Point p2(1, 2);
- (++p2).ShowPosition();
- return 0;
- }
결가 값을 보면 (p1++).ShowPosition(); 이 부분에 1 2 값을 출력하는 것을 알 수 있다. p1객체 뒤에 증가
연산자가 왔으므로, 이것은 후 증가 의미를 가지기 때문이다. 그후 p1을 출력해보면, 후 증가의 값을 눈으로 확인해볼 수 있는
것이다.
그럼 증가 하기 이전의 값을 얻기 위한 함수는 어떻게 만들까? 그 함수는 바로 이 부분이다.
- Point Point::operator++(int){
- Point temp(x, y); // Point temp(*this);
- x++;
- y++;
- return temp;
- }
증가하기 이전에 값을 만든 다음에 (temp) 그 다음에 값을 증가
시키고,
리턴 할때는 증가 하기 이전에 객체를 이전하면 된다. 이 함수의 경우에는
참조로 리턴이 안됐는데, 이 함수는
참조로 리턴 할 수 없다.
왜냐? 참조로 리턴(return) 할 수 없는것이 무엇인가? 바로 지역
변수, 지역객체 이다. 여기서 temp는
지역적으로 선언된것이므로 참조로 리턴 될 수 없다. 만약 리턴 된다고
해도 리턴 되고 나서 이 리턴값은 바로 사라지기 때문이다. 이게 바로 연산자 오버로딩의 한계점이라고 할 수
있다.
'개발지식창고 > C++' 카테고리의 다른 글
쉬프트 연산자 오버로딩 (cout, cin, endl) (0) | 2010.11.14 |
---|---|
연산자 오버로딩 - 교환법칙의 성립과 임시객체(Temporary Object) (0) | 2010.11.14 |
연산자 오버로딩 (Operator Overloading)의 두가지 방법 (0) | 2010.11.14 |
상속의 마지막, 다중 상속(Multiple Inheritance) (0) | 2010.11.14 |
가상 함수 동작 원리와 단점 (0) | 2010.11.14 |