18. 연산자 오버로딩
TCPL++ 18장
18.2 연산자 함수
18.2.1 이항 연산자와 단항 연산자
기본적으로 이항 연산자를
이항 연산자(
aa@bb
)는 다음과 같이 2가지 형태로 해석된다.aa.operator@(bb)
(멤버함수)operator@(aa,bb)
(비멤버함수)
전위형 단항 연산자(
@aa
) 는 다음과 같이 2가지 형태로 해석된다.aa.operator@()
(멤버함수)operator@(aa)
(비멤버함수)
후위형 단항 연산자(
@aa
) 는 다음과 같이 2가지 형태로 해석된다.aa.operator@(int)
(멤버함수)operator@(aa, int)
(비멤버함수)
18.2.2 연산자의 사전 정의된 의미
일부 기본 제공 연산자는 동일한 인자들에 대해 다른 연산자들의 조합과 같은 의미를 지니게 된다. → 예를 들어 a 가 정수라면,
++a
는a+=1
을 의미한다. → 이는 결국a = a+1
을 의미한다.이런 관계는 사용자가 정의한 연산자에 영향을 받지 않는다. → 예를 들어 컴파일러는
operator+()
와operator=()
의 정의에서operator+=()
의 정의를 알아서 생성해내지 못한다.
18.2.3 연산자와 사용자 정의 타입
연산자 함수는 멤버이거나 사용자 정의 타입의 인자를 최소 한 개는 받아들여야 한다.
자신의 첫 번째 피연산자로 기본 제공 타입을 받아들이게 만들어진 연산자 함수는 멤버 함수가 될 수 없다. →
2.operator+(aa)
를 의미하게 정의해주는 int 클래스가 없기 때문이다. → 설사 있더라도,2+aa
와aa+2
를 처리하려면 두 개의 다른 멤버 함수가 필요할 것이다.
18.2.4 개체 전달
연산자를 정의할 때 대개
a=b+c
같이 관용적 표기법을 제공하고 싶어 한다.이를 위해서, 인자를 전달하는 방법과 리턴값을 전달하는 방법에는 제한이 있다.
먼저 인자를 전달하는 방법에는 두 가지 선택이 있다.
값에 의한 전달
예를 들어 작은 개체에 대해서는 값에 의한 전달이 대개는 현실적이고, 가장 최적의 성능을 보인다.
참조에 의한 전달
좀 더 큰 개체는 참조에 의해서 전달된다. → 인자를 변경하지 않기로 되어있는 개체 전달에는 const 를 사용한다.
대개 연산자는 결과를 반환한다. 새로 생성된 개체의 포인터나 참조자를 반환하는 것은 대체적으로 좋지 못한 생각이다. → 포인터를 사용하면 표기 문제(관용적인 표기로 사용하기 어렵다)가 발생하고, 힙 영역에 있는 개체의 참조는 메모리 관리 문제도 낳는다.
이후 내용은 멘토링 이후 추가 …..
18.3 복소수 타입
복소수(
complex
) 타입을 연산자 오버로딩을 통해 구현해보자복소수는 실수와 허수가 결합된 형태를 의미하며, 만약 복소수 타입을 만든다면, 아래와 같이 사용하기를 원할 것이다.
18.3.1 멤버 및 비멤버 연산자
필자는 개체의 표현을 직접적으로 조작하는 함수를 최대한 자제하는 편인데, →
+
연산자 내부에서+=
연산이 이루어지도록 설계하자이를 달성하려면, 클래스 자체 내에서는
+=
같이 태생적으로 자신의 첫 번째 인자의 값을 변경하는 연산자만을 정의해야 한다.그렇다면
+
같이 자신의 인자의 값을 기반으로 새로운 값을 산출하는 연산자들은 클래스 외부에서 정의돼야 한다.
위 선언이 주어지면 아래와 같이 사용할 수 있다 . →
r1
과r2
의 효율성 차이를 제외하면 연산을 동일하게 동작한다.
18.3.2 혼합 모드 산술 연산
z
가complex
인 경우에2+z
를 처리하면서 연산자+
가 서로 다른 타입의 피연산자를 받아들이게 정의해야 한다. → **혼합 모드 산술 연산(mixed-mode arithmetic)**이 필요하다는 것이다!이를 위해 아래와 같이 적절한 연산자를 추가해주면 된다.
operator+()
의 세 가지 변형은 complex 바깥에서 정의될 수 있다.
에 대해서 이런 선언들이 주어지면 다음과 같이 작성할 수 있다.
18.3.3 변환
complex 변수의 대입과 초기화를 처리하기 위해서는 정수 또는 부동소수점 숫자를 complex 로 변환해야 한다.
그것이 가능하기 위해서는 다음과 같이 하나의 인자를 받아들이는 생성자를 제공해야 한다.
생성자 기본값을 사용해 코드를 줄이자
해당 생성자에서는 암시적으로 변환이 되는데, 암시적인 변환을 허용하고 싶지 않다면,
explicit
로 선언하면 된다.
18.3.3.1 피연산자의 변환
위에서 혼합 모드 산술 연산을 아래와 같이 구현했다.
하지만 이런식으로 타입이 늘어날 때마다 연산자 함수가 늘어나는 것은 유지보수가 어려워진다.
때문에, 변환을 이용해서 유연하게 해결해나가야 한다.
현재 우리의 complex 클래스는 double 을 complex 로 변환하는 생성자를 하나 가지고 있다.
따라서 complex 에 대해 동등 연산자 버전을 딱 하나만 선언할 수 있다. → 물론 변환에 의한 오버헤드가 생겨날 수 있지만, 그런 이슈가 중요하지 않은 경우에는 충분히 변환을 고려할 수 있다. → 암시적 변환을 원하지 않는다면
explicit
을 사용할 수 있다.
18.3.4 리터럴
기본 제공 타입으로 된 리터럴이 있다.
예를 들어
1.2
와12e3
은 double 타입의 리터럴이다.
complex 에 대해서도 생성자
constexpr
을 선언하면 기본 제공 타입의 리터럴과 비슷해진다.해당 코드는 complex 도 기본 제공 타입의 리터럴과 마찬가지로 컴파일 타임에 구성 부분을 기반으로 생성될 수 있다.
생성자가 간단하고 인라인인 경우(특히,
constexpr
인 경우) 리터럴 인자를 통한 생성자 호출을 그냥 리터럴로 보아도 무방하다.
Last updated