프로그래밍/cpp

연산자 오버로딩 C++

콘파냐 2014. 3. 19. 21:04
반응형

일반적인 연산을 살펴보면 오버로딩과 비슷한 규칙을 살펴볼 수 있다. 피연산자의 타입이 달라지더라도 알아서 계산이 된다는 것이다.

예를들어 정수+정수 의 연산결과는 정수가 되고, 실수+실수 의 연산결과는 실수가 된다. 이렇게 피연산자의 타입을 알아서 체크하고 그에 맞는 +연산을 하는 것은 마치 함수가 인수의 타입에 따라서 작동하는 오버로딩과 비슷하다 볼 수 있다.

이렇게 살펴본 사실은 실제로 사실이다. 다시말해 '+'는 피연산자의 타입에 따라서 오버로딩 되어있다. 하지만 char*타입과 같은 문자나 클래스 타입에 대해서는 작동하지않는다. 그렇다면 사용자가 직접 이런 연산자들을 오버로딩하는 법이 있을까?



15~18라인은 연산자 오버로딩코드이다. 일반적인 함수의 선언과 비슷한데 눈여겨볼 점은 호출시의 모양이다. main함수내에서 연산을 일반적인 사칙연산을 하는 것처럼 A+B 하였다. 그리고 C에 그 값을 대입하였다. 이 대입부분은 우리가 알고 있는 디폴트 복사 생성자의 호출이 있음을 명심하자. 



그럼 연산자 오버로딩구문을 살펴보자.

연산자 오버로딩도 리턴타입과 함수명 인수가 존재한다. 그럼 일반적인 4칙연산과 비교해서 짝을 맞춰보자.

const myTest operator+(const myTest &T) cosnt   와 A+B 를 짝을 맞춰보면 T에 해당하는 것이 B가 되고 A는 호출 객체가 된다.

실제  A.operator+(B); 이렇게 호출해야 할 것이다. 하지만 우리는 가독성을 위해서 A+B; 를 같은 것으로 약속하고 사용한다.



전체적인 형태는 잡혔다. 연산자 오버로딩도 복사생성자의 경우와 마찬가지로 원래의 형태는 우리가 알고 있는 문법을 그대로 사용한다. 단지 가독성을 위해 = 과 +로 매칭시키는 것 뿐이다. 주의할 점은 여기서는 + 연산을 기준으로 설명하지만, 연산자의 종류, 타입에 따라서도 구현하는데 다른 주의점들이 생길수 있다는 것을 알아두자.


그럼 const의 3군데에 위치한 의미와 인수를 레퍼런스로 전달하는 의미를 살펴보겠다.



우리가 살펴볼 부분은 리턴타입, 인수타입 함수타입이다. 각각 const를 사용한 의미가 있고, 이 부분은 연산자마다 달라질 수 있는 부분이다. 여기서는 +연산자를 기준으로 설명하는데 + 연산자의 특성으로인해 세군데 모두 const가 붙는다. 하지만 인수타입의 경우는 대부분의 연산자가 공통적으로 레퍼런스를 붙이는 것이 좋은 이유가 있기 때문에 인수타입에 대해서 먼저 설명하기로 하겠다.


인수타입이 레퍼런스인 이유

뭐 전달하고자 하면 객체를 값으로 또는 포인터로 전달할 수 있을 것이다. 굳이 레퍼런스로 전달하는 이유는 다음과 같다.

첫째, 포인터로 전달을 하게 되면 위 코드가 이렇게 바뀐다 C=A+&B; //가독성 또는 직관적이지 못하다.

둘째, 값을전달하면 가독성에선 문제는 없지만, 객체의 크기가 커질수록 시스템 자원을 많이 잡아먹게 된다.

레퍼런스로 전달하게 되면 위 두가지경우가 해결 된다.


한가지 의문점은 해결했으니 const의 위치에 따른 의미를 생각해보자.


2014/01/14 - [프로그래밍/C언어] - 상수포인터, 포인터상수, const


const는 위치에 따라서 해석하는 방식이 다르고 또한 값의 조작을 막는 일종의 안전장치다. 좀더 쉽게 접근하기위해 우리는 덧셈연산에서 값이 변하면 안되는 부분을 먼저 파악해보기로하자.

C=A+B; // 변하지 않는 부분 A, B, A+B

매칭시켜보면 A는 호출객체이고 B는 인수가 된다. A+B는 리턴타입이다.


C에 대입되기 전까지  A,B,A+B의 값이 변하는 것은 의미도 없고 연산의 결과가 달라지게 되기 때문에 안된다. 위에서 const를 일종의 안전장치라 생각하라고 했다. 굳이 안붙여도 연산이 될 것이다. 하지만, 우리가 알지 못하는 예외(여기서는 중간에 값이 변하는)를 원천 차단하는 것이 더 신뢰성 있는 코드가 될 것이다.

그럼 한가지씩 살펴보자. 순서는 코드가 실행되는 순서대로 하겠다.

③에 붙은 const는 이 함수가 상수멤버함수임을 나타낸다. 호출객체(여기서는 A)의 멤버변수를 조작하지 않겠다는 선언이다.

②에 붙은 const는 함수내에서 실인수를 상수화 시켜준다.(상수취급한다)

①에 붙은 const는 리턴타입을 상수취급하겠다는 말인데, A+B는 여기서는 함수 내부에서 선언된 임시객체의 값이 된다. 합의 결과가 저장되고 리턴될때 상수로 취급되어 C에 대입되고 조작을 허용하지 않는다.


증감연산자 ++,--의 경우는 호출객체만 있고, 인수는 받아들이지 않는 형태가 될 것이다. 그리고 자신의 값을 증감시키기 때문에 호출객체의 멤버변수를 바꾸는 연산으로 오버로딩 될 것이다. 때문에 이런연산의 경우는 인수가 없는 형태가 되고 ③의 const는 없어야 할 것이다. 

이같은 원리로 += 의 예를 들어보면 나머지는 같고 ③의 const 지우면 될 것이다. 그런데 이경우 리턴타입은 없어야 할 것이다.

예를들어 A+=1 의 리턴타입이 있을 필요가 없기 때문이다.


이렇게 연산의 성격에 따라서 적절하게 함수를 오버로딩해야한다.


추가:리턴타입이 값인경우와 레퍼런스인 경우를 구별해야하는데, 보통 내부의 임시객체,타입을 리턴하는 경우 값으로 리턴해야한다. 레퍼런스나 포인터로 리턴하게되면 함수가 리턴된후 스택을 다음함수가 이용하는 순간 임시 객체, 변수는 날라가기 때문이다. 하지만 값으로 리턴하게되면, 사본이 생성된다. 즉 스택이 아닌 곳에 사본이 생성되어 19나 323같은 원리로 값을 사용할 수 있다.

여기서 A+B+K 같이 +를 두번 호출하게 되면 A+B가 값으로 리턴되는 경우는 제대로 실행되지만, 레퍼런스로 리턴되는 경우 리턴값이 다음 +호출시 레퍼런스가 가리키는 임시객체가 날라가게된다. (외부변수를 레퍼런스, 포인터로 받아서 다시 리턴하는 경우는 상관없다.)

반응형