본문 바로가기

Programming/C / C++

[C++] 단항 연산자 오버로딩

 증가/감소 연산자 오버로딩  
 이전에는 사칙연산인 이항연산자 오버로딩에 대해서 알아 봤는데 이번에는 단항 연산자인 증가(++), 감소(--) 연산자 오버로딩에 대해서 알아 보자. 만약 P객체가 증가가 된다면 멤버함수와 전역함수에서는 C++의 약속에 의해 어떻게 표현 될까? 이전의 이항 연산자 오버로딩때와 마찬가지로 아래와 같이 됨을 알 수 있을 것이다. 
그럼 단항 연산자 오버로딩의 예제 소스코드를 한번 살펴보자. 
 여기에서 눈여겨 봐야 될 것은, 24번째줄에 operator++ 멤버 함수는 리턴 타입이 포인터의 참조를 리턴 하고 있는것을 알 수 있다. 물론 전역도 마찬가지로 참조를 리턴해주고 있다. 그럼 왜 참조형을 리턴해주는 것일까? 그것은 바로 40번째 라인과 같은 ++(++p) 형태의 연산을 허용해 주기 위해서이다.  ++(++p)는 어떤 의미를 갖는가? 
 여기 n이라는 변수가 있다. ++(++n) 연산을 하면, ( )괄호 연산에서 n은 2에서 3의 값이 되고 이 3이 된 n을 ++( n)  이와 같이 증가 시켜 줘서 결국 4란 값이 되는것이다. 여기서 알 수 있는 것은 괄호 연산 앞의 증가 연산은 어디에 영향을 미치느냐 하면은, 바로 변수 n에 영향을 미치기 때문에 이런 증가 연산이 성립이 되는 것이다. 결국 변수 or 객체를 기준으로 증가 연산을 하는 것이다. 

 따라서 ++(++p) 연산에서도 괄호 안의 연산인 p.operator++() 함수 호출이 끝나고 나서 이 자리에 p가 그대로 리턴이 돼야 이런 증가 연산이 제대로 이루어 질 수 있다. 
 멤버 함수 정의부를 보면 return 타입이 *this인 것을 알 수 있다. 여기서의 this는 Point 객체의 포인터를 의미하는데, *를 붙였으니까 Point객체 자신을 의미한다. 그럼 나 자신을 무엇으로 리턴 하느냐? 바로 Point& 즉, 참조로 리턴을 하는 것이다. 
  괄호 안은 p.operatr++()이 되고 이 연산이 끝나고 p의 참조를 리턴하는데 p의 참조는 p와 같다. ++(p참조) -> p참조 . operator++() 이 것이 된다. p참조는 p자신 이므로, p.operator++() 이 되는 것과 같다. 그래서 결과가 ' 3 4 '로  연산이 제대로 되는 것이다. 


 만약 참조로 리턴 안하고 Point로 리턴 한다면 어떻게 될까? 
 그냥 Point를 리턴 한다면, 나 자신을 복사 해서 리턴 하는것이다. 그래서 ++(++p) 에서 괄호 안의 연산후에 이 자리에 오는 것은 p가 아니라 p객체의 복사본이 리턴되는것이다. 그리고 p객체의 복사본을 가지고 ++연산을 하게 되는것이다. 결과적으로 리턴하는 순간에 새로운 객체를 만들어서 리턴하는 꼴이 되므로 p객체는 단지 한번만 증가 하게 되는 것이다. (이것이 문제가 된다)


 선연산과 후연산의 구분  
 단항 연산자 ++, --를 변수나 객체의 앞에 붙이느냐 뒤에 붙이느냐에 따라 증가를 먼저 하고 연산을 할것인가, 연산을 먼저 한 후, 값을 증가 시킬 것인지 나뉘게 된다. 
 - ++ p : 변수의 값을 먼저 증가 시킨후 연산
 - p++ : 연산후 값 증가
 우리가 단항 연산자 오버로딩을 설계할 때, 이처럼 문법상 기본 자료형이 하는 일을 따라 간다면 우리는 선연산 후연산이 다르게 동작 되어야 하는것을 알 수 있는 것이다. 그래서 우리는 호출 되는 함수의 구분을 위해 C++은 또 하나의 약속을 해야 하는 것이다.  다음과 같이 말이다. 
 - ++p 은 p.operator ++( );
 - p++ 은 p.operator ++(Data Type);

 여기서 선언된 데이터 타입은(int, double 등등의...)  ++ 연산을 구분 지어 주기 위해서만 의미를 가진다. 예제 소스를 보자.

 결가 값을 보면 (p1++).ShowPosition(); 이 부분에 1 2 값을 출력하는 것을 알 수 있다. p1객체 뒤에 증가 연산자가 왔으므로, 이것은 후 증가 의미를 가지기 때문이다. 그후 p1을 출력해보면, 후 증가의 값을 눈으로 확인해볼 수 있는 것이다. 

 그럼 증가 하기 이전의 값을 얻기 위한 함수는 어떻게 만들까? 그 함수는 바로 이 부분이다. 
 
증가하기 이전에 값을 만든 다음에 (temp) 그 다음에 값을 증가 시키고, 
리턴 할때는 증가 하기 이전에 객체를 이전하면 된다. 이 함수의 경우에는 참조로 리턴이 안됐는데, 이 함수는 
참조로 리턴 할 수 없다. 
 왜냐? 참조로 리턴(return) 할 수 없는것이 무엇인가? 바로 지역 변수, 지역객체 이다. 여기서 temp는 
지역적으로 선언된것이므로 참조로 리턴 될 수 없다. 만약 리턴 된다고 해도 리턴 되고 나서 이 리턴값은 바로 사라지기 때문이다. 이게 바로 연산자 오버로딩의 한계점이라고 할 수 있다.