클래스에서 암시적 타입 변환을 지원하는 것은 일반적으로 못된 생각이다.
하지만 예외도 있는데, 대표적인 경우가 숫자 타입을 만들 때이다.
예를들면 유리수를 나타내는 클래스를 만들고 있다면, 정수->유리수의 암묵적 타입 변환은 허용하자고 판단해도 된다. 왜냐하면 C++에서의 int -> double 변환과 별반 다를게 없기 때문이다.
Rational = 유리수를 나타내는 클래스
그럼 +,* 같은 기본연산은 지원해줘야 할 것이다. => 멤버함수, 비멤버함수, 비멤버-비프렌드함수 중에 뭘로 해야 할까
23에서 비멤버-비프렌드 함수를 쓰자고 배웠지만, 일단 멤버함수로 만들어보자.
이러면 왠만한 상황에선 *이 잘 될 것이다. 하지만 혼합형 수치 연산도 가능했으면 좋겠다. (mixed-mode 지원) => int와 같은 것과도 곱하고 싶다. 즉
일단 저 상태에선
r1 = r2 * 2; // good
r1 = 2 * r2; // error -> 2.operator*(r2) -> 없음 -> operator*(2,r2); -> 없음
근데 곱셈은 당연히 교환법칙 성립해야됨.
왜 첫번째는 성공했나?
r2.operator(2) => Rational 객체인 r2는 operator* 함수를 멤버로 가지고 있어서 호출함 -> 인자 2는 어떻게 먹음? -> 암시적 타입 변환 때문이다.
컴파일러는 우리가 이 함수에 int를 넘겼고, 함수 쪽에선 Rational을 요구한다는 사실을 안다. -> int를 Rational 생성자의 인자로 넘겨서 int->Rational 로 바꾸는 것이 가능하다는 것도 안다. -> 이 코드로 반환됨
const Rational temp(2);
result = oneHalf * temp;
물론 컴파일러가 이렇게 동작한 것은 생성자가 명시호출(explicit)이 아니였기 때문이다. 만약 explicit이였으면 2를 Rational(2)로 암묵적으로 바꾸는 것이 불가능함.
explicit으로 하면
둘 다 에러남;
대신 둘 다 일관성이 있게 에러가 난다. 하나라도 건지는 것 보다 일관성을 챙기는게 더 중요함
일관성은 챙겼다. 그럼 연산이 되게 해야된다. 어떻게??
이렇게 하면 되지;
r1 = r2 * 2; // good
r1 = 2 * r2; // error -> 2.operator*(r2) -> 없음 -> operator*(2,r2); -> 없음
1번은 되고 왜 2번은 안되는데 => 암시적 타입 변환이 일어날려면 매개변수 리스트에 있어야 된다. 두번째 문장에서 2는 2.operator*(r2) 에선 객체 취급(매개변수 자체가 아님) , operator*(2,r2) 에선 이에 맞는 비멤버 함수가 없음(하지만 매개변수는 맞음) => 비맴버 함수를 그냥 만들면 된다.
이때 비맴버 함수라 데이터 맴버에 접근을 못하니, 데이터 맴버의 값을 반환해주는 examiner 함수를 통해 값을 불러옴(함수를 사용하여 멤버에 대한 접근성을 세밀하게 컨트롤 가능, 항목 22 참조)
friend를 쓰면 되지 않나 -> 이미 public으로 완벽히 구현 가능한데 굳이?
friend는 솔직히 안쓰는게 정신건강에 좋음. 가끔 반드시 써야할때가 있긴함(static function으로 구현이 안될 때, iostream의 operator>> 같은 것) 하지만 맴버함수의 반댓말은 비맴버함수이다.
맴버함수 vs 정적 맴버함수 vs 프랜드 함수 vs 비맴버-비프랜드함수
차이를 명확하게 알자
======================================================================
- 어떤 함수에 들어가는 모든 매개변수(this가 가르키는 객체 포함)에 대해 타입 변환을 해 줄 필요가 있다면, 그 함수는 비멤버여야 한다.
'Effective C++ > 4. 설계 및 선언' 카테고리의 다른 글
항목 25: 예외를 던지지 않는 swap에 대한 지워도 생각해 보자. (0) | 2021.04.24 |
---|---|
항목 23: 함수보다는 비멤버 비프렌드 함수와 더 가까워지자 (0) | 2021.04.24 |
항목 22: 데이터 맴버가 선언될 곳은 private 영역임을 명심하자. (0) | 2021.04.24 |
항목 21: 함에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자. (0) | 2021.04.24 |
항목 20: '값에 의한 전달'보다는 '상수 객체 참조자에 의한 전달' 방식을 택하는 편이 대개 낫다. (0) | 2021.04.24 |