|
프렌드 함수는 보통 연산자 겹지정(operator overloading)을 위해 사용됩니다.
다음과 같은 2차원 벡터를 나타내는
Vector(std::vector가 아닙니다.)라는 클래스를 정의해보죠.
(다음은 'C++ 기초 플러스'(성안당)에 있는 내용을 정리한 겁니다.)
멤버 함수로
두 벡터의 덧셈, 뺄셈인 +과 - 연산과
상수를 곱하는 * 연산을 정의한다면 다음과 같습니다.
class Vector
{
private:
double x, y;
public:
Vector(double _x, double _y) : x(_x), y(_y) {};
Vector(const Vector& v) { x = v.x; y = v.y; }
double GetX() { return x; }
double GetY() { return y; }
void SetX(double _x) { x = _x; }
void SetY(double _y) { y = _y; }
Vector operator+(const Vector& b) { return Vector(x + b.x, y + b.y); }
Vector operator-(const Vector& b) { return Vector(x - b.x, y - b.y); }
Vector operator*(double n) { return Vector(n * x, n * y); }
}
사용할 때는
Vector a(1.0, 2.0), b(3.0, 4.0),
sum = a + b, diff = a - b;
라고 하면, 컴파일러가 다음과 같이 바꿉니다.
sum = a.operator+(b), diff = a.operator-(b);
물론, sum은 (4.0, 6.0)은 diff는 (-2.0, -2.0)가 됩니다.
operator*()를 테스트하려면
Vector mul = a * 2.0;
이라고 쓰면,
mul = a.operator*(2.0);
가 되어 mul이 (2.0, 4,0)이 되는데요,
만약 다음과 같이
Vector mul = 2.0 * a;
라고 쓰면, 어떻게 될까요?
컴파일러가 다음과 같이 변형하게 되는데,
mul = 2.0.operator*(a);
물론, 컴파일 에러가 나게 됩니다.
이런 문제를 해결하려면,
operator*()를 멤버 함수가 아니라 다음과 같이 프렌드 일반 함수로 정의하면 됩니다.
class Vector {
private:
...
public:
...
friend Vector operator*(const Vector& a, double n);
friend Vector operator*(double n, const Vector& a);
}
// 이렇게 짧은 함수는 inline을 붙이면 성능 향상에 도움이 됩니다.
inline Vector operator*(const Vector& a, double n)
{
return Vector(a.x * n, a.y * n);
}
inline Vector operator*(double n, const Vector& a)
{
return Vector(n * a.x, n * a.y);
}
그러면
Vector mul = 2.0 * a;
가
Vector mul = operator*(2.0, a)
로 바뀌므로 문제 없이 컴파일됩니다.
이번에는 다른 문제를 생각해보죠.
연산자 겹지정을 할 때는, 멤버 함수 대신 프렌드 일반 함수를 쓰실 것을 권합니다.
operator+()와 operator-()의 경우도,
class Vector {
...
friend Vector operator+(const Vector& a, const Vector& b)
friend Vector operator-(const Vector& a, const Vector& b)
...
}
inline Vector operator+(const Vector& a, const Vector& b)
{
return Vector(a.x + b.x, a.y + b.y);
}
inline Vector operator-(const Vector& a, const Vector& b)
{
return Vector(a.x - b.x, a.y - b.y);
}
라고 쓰면, 대칭적인 코드가 되기 때문에 가독성이 더 좋습니다.
또다른 예를 들어보죠.
Vector를 출력하려면 어떻게 해야 할까요?
예를 들어 다음과 같이 멤버함수로 정의해보죠.
class Vector
{
...
void operator<<(ostream& os, const Vector& v);
...
};
inline void Vector::operator<<(ostream& os, const Vector& v)
{
os << "(" << x << "," << y << ")";
}
라고 한다면,
Vector v;
일때
cout << v;
라고 쓸 수는 없습니다.
cout.operator<<(v);
로 변형하면
cout::operator<<(const Vector& v)
가 호출되어야 하는데, 이런 함수는 정의되어 있지 않기 때문이죠.
그러므로, 대신
v << cout;
이라고 쓸 수 밖에는 없습니다. 상당히 어색하지요. -_-;
그래서 다음과 같이 프렌드 일반 함수로 정의합니다.
class Vector
{
...
friend ostream& operator<<(ostream& os, const Vector& v);
...
};
inline ostream& operator<<(ostream& os, const Vector& v)
{
os << "(" << x << "," << y << ")";
return os;
}
여기서 return 값을 ostream& 으로 한 것은 다음과 같이 연속해서 출력하는 경우를 위해서 입니다.
cout << v << "is vector." << endl;
ostream& 리턴값이 없다면,
cout << v;
라고 쓸 수 밖에는 없죠.
하여튼 여기서는
operator<<(cout, v)
가 호출되므로 문제없이 컴파일 됩니다.
:
: 주의사항 : friend 함수는 클래스의 추상화에 위배가 되기때문에
: 많이 사용하면 안좋다고 그러네요..^^;
: 그러니까 꼭 필요할때만 사용하시면 됩니다..
이것은 추상화라기보다는 캡슐화라는 겁니다.
friend 함수는 클래스의 캡슐화에 위배되는 것이 아닙니다.
단지 멤버 함수를 다른 표현으로 바꾼 것에 불과합니다.
클래스 내부에서 (friend라는 키워드를 붙여서) 선언을 하지 않으면
쓸 수 없는 것은 멤버 함수와 마찬가지입니다.
다시 한 번 강조하지만, 연산자 겹지정을 할 때는 멤버 함수대신 프렌드 일반 함수를 사용하세요.
코드의 가독성도 높아지고, 코딩 에러도 줄일 수 있는 좋은 방법입니다.
|