클래스의 friend 함수는 해당 클래스의 범위(scope) 외부에서 정의되지만, 클래스의 모든 private 및 protected 멤버에 접근할 수 있는 특별한 권한을 가집니다. friend 함수의 원형(prototype)이 클래스 정의 내부에 선언되긴 하지만, friend는 엄밀히 말해 멤버 함수가 아닙니다.
friend로 지정할 수 있는 대상
friend는 일반 함수, 함수 템플릿, 멤버 함수가 될 수 있으며, 클래스나 클래스 템플릿도 friend로 지정할 수 있습니다. 클래스를 friend로 선언하면 그 클래스 전체와 모든 멤버가 friend 권한을 갖게 됩니다.
friend 함수 선언 방법
함수를 클래스의 friend로 선언하려면 클래스 정의 안에서 함수 원형 앞에 friend 키워드를 붙이면 됩니다.
class Box {
double width;
public:
double length;
friend void printWidth(Box box);
void setWidth(double wid);
};한 클래스의 모든 멤버 함수를 다른 클래스의 friend로 만들 수도 있습니다. 예를 들어 ClassTwo의 모든 멤버 함수를 ClassOne의 friend로 선언하려면, ClassOne의 정의 안에 다음 한 줄을 추가하면 됩니다.
friend class ClassTwo;예제 코드
#include <iostream>
using namespace std;
class Box {
double width;
public:
friend void printWidth(Box box);
void setWidth(double wid);
};
// 멤버 함수 정의
void Box::setWidth(double wid) {
width = wid;
}
// 참고: printWidth()는 어떤 클래스의 멤버 함수도 아닙니다.
void printWidth(Box box) {
/* printWidth()는 Box의 friend이므로
이 클래스의 모든 멤버에 직접 접근할 수 있습니다 */
cout << "Width of box : " << box.width << endl;
}
// 프로그램의 main 함수
int main() {
Box box;
// 멤버 함수를 통해 박스의 너비 설정
box.setWidth(10.0);
// friend 함수를 사용해 너비 출력
printWidth(box);
return 0;
}실행 결과
Width of box: 10printWidth() 함수는 Box 클래스의 멤버가 아니었음에도 불구하고, private 멤버 변수인 width에 직접 접근해 값을 출력할 수 있었습니다. 이처럼 friend는 캡슐화의 경계를 선택적으로 완화해 주는 강력한 도구입니다.
friend를 활용하면 좋은 상황
friend를 남용하면 캡슐화가 깨지고 코드 간 결합도가 높아질 수 있으므로 신중하게 사용해야 하지만, 다음과 같은 경우에는 특히 유용합니다.
- 연산자 오버로딩:
operator<<나operator>>처럼 왼쪽 피연산자가 ostream/istream 객체인 연산자를 구현할 때 friend 함수가 사실상 필수적입니다. - 밀접하게 협력하는 두 클래스: 서로의 내부 데이터를 자주 공유해야 하는 클래스들이 있을 때, getter/setter를 남발하는 것보다 friend 선언이 더 깔끔할 수 있습니다.
- 팩토리 함수나 헬퍼 함수: 객체 생성 과정에서 내부 멤버에 접근해야 하는 유틸리티 함수에 friend 권한을 부여할 수 있습니다.
정리
friend는 클래스 외부의 함수나 다른 클래스에게 private/protected 멤버 접근 권한을 부여하는 C++의 접근 제어 메커니즘입니다. 멤버 함수로 만들기 어려운 연산자 오버로딩이나 긴밀한 클래스 간 협력이 필요한 상황에서 활용하되, 캡슐화 훼손을 최소화할 수 있도록 최소한의 범위로만 지정하는 것이 바람직합니다.