[C++] 타입 변환이 모든 매개변수에 대해 적용되어야
한다면 비멤버 함수를 선언하자.
유리수를 나타내는 클래스를 만들고 있다.
이 클래스를 디자인할 때 정수에서 유리수로
암시적 변환은 허용하자고 판단을 했다.
int -> double 변환과 별반 다르지 않기 때문에.
![](//i1.daumcdn.net/deco/contents/emoticon/things_10.gif?v=2)
class Rational {
public:
Rational ( int numerator = 0 , int denominator = 1 );
// explicit으로 선언하지 않았다.
int numerator() const; // 분자 접근 함수
int denominator() const; // 분모 접근 함수
private:
...
}
![](//i1.daumcdn.net/deco/contents/emoticon/things_11.gif?v=2)
유리수를 나타내는 클래스이기 때문에 덧셈이나
곱셈 등의 수치연산은 기본으로 지원해야 할 것이다.
멤버 함수로 연산자 오버로딩을 해보자.
const Rational operator* (const Rational& rhs) const;
이로써 곱셈 연산이 가능해진다.
Rational oneEighth (1 , 8); // 가능
Rational oneHalf(1, 2); // 가능
Rational result = oneHalf * oneEighth; // 가능
result = result * oneEighth; // 가능
![](//i1.daumcdn.net/deco/contents/emoticon/things_12.gif?v=2)
이제 혼합형 수치 연산을 해보자.
유리수를 int(정수)와 곱셈을 가능하게
하는 것도 어떻게 보면 당연한 일이다.
result = oneHalf * 2; // 가능
result = oneHalf.operator(2); // 가능
result = 2 * ontHalf; // 에러
result = 2.operator(oneHalf); // 에러
![](//i1.daumcdn.net/deco/contents/emoticon/things_13.gif?v=2)
첫 번쨰 줄에서 oneHalf 객체는 operator* 함수를 멤버로
가지고 있는 클래스의 인스턴스이기 때문에, 컴파일러는
이 함수를 호출한다. 하지만 두 번째 줄에서 정수 2에는
클래스 같은 것이 연관되어 있지 않기 때문에 operator*
멤버 함수도 있을리가 없다. 따라서 컴파일러는 비멤버
버전의 operator*를 찾아본다.
result = operator*(2,oneHalf);
비멤버 버전의 operator*가 없기 때문에 실패.
![](//i1.daumcdn.net/deco/contents/emoticon/things_14.gif?v=2)
여기서 위의 성공 케이스를 다시 보도록 하자.
매개변수가 정수 2인데 Rational 객체를 받도록
되어 있는 operator*의 호출을 가능하게 한다.
이게 어떻게 가능한 것일까?
바로 암시적 타입 변환에 의해 가능하게 되는 것이다.
컴파일러는 이 int를 Rational 클래스의 생성자에 주어
호출하면 Rational로 둔갑시킬 수 있다는 사실도 알고 있다.
![](//i1.daumcdn.net/deco/contents/emoticon/things_16.gif?v=2)
따라서
const Rational temp(2);
result = oneHalf * temp;
이런식으로 동작을 하게 된다.
물론 생성자를 명시호출 (explicit)로 선언되었다면 모두 불가능.
![](//i1.daumcdn.net/deco/contents/emoticon/things_09.gif?v=2)
자 다시 돌아와서 둘 다 비명시호출을 했는데도 하나는 되고
하나는 왜 되지 않는지 고민해 보도록 하자.
result = oneHalf * 2; --> 비명시호출 생성자
result = 2 * ontHalf; --> 비명시호출 생성자인데도 안됨
![](//i1.daumcdn.net/deco/contents/emoticon/things_11.gif?v=2)
결론
암시적 타입 변환이 먹혀들려면 매개변수 리스트에 들어있어야만 한다.
전자의 경우 매개변수 리스트에 있는 객체가 쓰이고 있지만
후자는 그렇지 않다. operator*의 구현부를 살짝 보자면
this * rhs 두 개의 매개 변수로 이루어져 있을 것인데
this란 놈은 매개변수 리스트에 없고 rhs란 놈만 있다.
따라서 매개변수 리스트에 있는 rhs란 놈만 암시적 타입변환이 일어난다.
암시적 타입 변환이 모든 매개변수에 대해서 이루어 지지 않고 있다.
그렇다면 모든 인자에 대해 암시적 타입변환을 수행하도록 하려면
어떻게 해야할까? operator*를 비멤버 함수로 만들어버리면 된다.
![](//i1.daumcdn.net/deco/contents/emoticon/things_10.gif?v=2)
const Rational operator* (const Rational& rhs, const Rational& rhs){
return Rational (lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denomiator());
}
Rational oneForth (1, 4);
Rational result;
result = oneFourth * 2; // 가능
result = 2 * oneFourth; // 가능
![](//i1.daumcdn.net/deco/contents/emoticon/things_02.gif?v=2)
위의 함수는 비멤버이지만 프렌스 함수로 선언해야
하는 것 아닌가 의아해 할 수도 있다. 하지만 위의
operator*는 클래스의 private 부분을 하나도
건드리지 않고 있다. 오로지 public 부분만을
사용하기 때문에 프렌드 선언은 적절하지 않다.
--> 프렌드 함수는 피할 수 있으면 피할 것
![](//i1.daumcdn.net/deco/contents/emoticon/things_14.gif?v=2)
POINT!!
어떤 함수에 들어가는 모든 매개변수(this도 포함)에 대해
타입 변환을 해 줄 필요가 있다면, 그 함수는 비멤버이어야 한다.