[C++] 항목 24: 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자.
카테고리: Cpp
태그: Cpp
이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역
📦 4. 설계 및 선언
👉🏻 항목 24: 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자.
아래는 유리수를 나타내는 클래스를 구현한 코드이다:
class Rational {
public:
// int->Rational 암시적 변환을 위해,
// explicit 을 붙이지 않았다.
Rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;
// 연산을 멤버 함수에 선언함
const Rational operator*(const Rational& rhs) const;
private:
...
};
유리수의 곱셈과 같은 연산은 멤버 함수에 선언해야 자연스러워 보인다.
그런데 항목 23에서 배웠던 것을 생각해보면 비멤버 비프렌드 함수로 두어야 할 것 같기도 하다.
우선 아래를 살펴보자.
✅ operator* 함수
Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth; // 성공
result = result * oneEighth; // 성공
위의 코드는 올바르게 작동한다.
이번엔 혼합형 수치 연산을 지원하고 싶다.
유리수 클래스인 Rational과 int를 곱해보자.
result = oneHalf * 2; // 성공
result = 2 * oneHalf; // 실패
여기서 성공하는 이유는 무엇이고, 실패하는 이유는 무엇일까?
1. result = oneHalf * 2;
이 코드를 함수 형태로 바꾸면 다음과 같다:
result = oneHalf.operator*(2)
그러나 이상한 점이 있다.
해당 연산자의 인자는 Rational 객체의 참조가 들어가야 한다.
마치 Rational temp = 2;
와 같은 동작을 하도록 한 것이다.
어떻게 가능한 것일까?
바로, 암시적 타입 변환(implicit type conversion)을 통해 가능하다.
중요한 점은 Rational 클래스의 생성자가 명시호출(explicit)로 선언되지 않았다는 것.
→ 그래서 컴파일러는 암시적으로 int를 Rational로 변환할 수 있게 된다.
정리하면 아래와 같이 동작한다:
const Rational temp(2); // 임시 Rational 객체 생성
result = oneHalf * temp; // oneHalf.operator*(temp);
2. result = 2 * oneHalf;
당연히 문제가 발생한다.
이 코드를 함수 형태로 바꾸면 아래와 같다:
result = 2.operator*(oneHalf);
정수 2에는 operator* 멤버 함수 같은 것이 있을 리가 없다.
여기서 컴파일러는 비멤버 버전의 operator*를 찾아본다.
result = operator*(2, oneHalf); // 실패
이 역시 존재하지 않는 함수다.
→ 그래서 컴파일 에러가 발생한다.
이를 해결하려면 어떻게 해야 할까?
✅ 해결 방법
class Rational {
... // Rational 안에 operator*를 지운다.
}
// 비멤버 함수로 선언하였다.
const Rational operator*(const Rational& lhs, const Rational& rhs) {
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
int main() {
Rational oneFourth(1, 4); // 성공
Rational result;
result = oneFourth * 2; // 성공
result = 2 * oneFourth; // 성공
}
operator*를 비멤버 함수로 만들고,
모든 인자에 대해 암시적 타입 변환을 수행할 수 있도록 만들면 된다.
❓ operator* 함수를 Rational 클래스의 프렌드로 두어도 될까?
이 함수에서는 그러면 안 된다.
→ operator*는 Rational의 public 인터페이스만을 사용하여 구현할 수 있기 때문
“멤버 함수의 반대는 프렌드 함수가 아닌, 비멤버 함수이다.” 라는 결론을 뽑을 수 있다.
또한, 프렌드 함수를 피할 수 있다면 피하도록 하자.
🧐 정리
- 어떤 함수에 들어가는 모든 매개변수(this 포인터가 가리키는 객체 포함)에 대해 타입 변환을 해줄 필요가 있다면, 그 함수는 비멤버이어야 한다.
- 멤버 함수의 반대는 프렌드 함수가 아닌 비멤버 함수이다.
- 프렌드 함수를 피할 수 있다면 피하자.
댓글남기기