friend
접근 제한자를 무력화하여 외부에서도 private, protected 멤버에 접근할 수 있도록 하는 키워드
- 주로 클래스에서 멤버 함수가 아닌 함수가 private한 멤버 변수에 접근하려고 할 때 사용한다.
- 클래스에서 멤버 함수와 전역 함수를 정의하는 것의 차이를 잘 모르겠다면 아래의 게시글을 참고한다.
friend 선언
- friend 키워드를 이용하여 만들 수 있는 형태는 전역 함수, 클래스, 멤버 함수 총 3가지이다.
friend 전역 함수
- friend 함수는 다음과 같이 선언한다.
- friend 키워드는 함수의 원형에서만 사용하고, 정의하는 부분에서는 적지 않는다.
friend 클래스명 함수명(매개변수);
// example
friend Point sum(int x, int y);
friend 클래스
- friend 클래스는 다음의 구문을 friend를 맺으려는 클래스 내에 정의한다.
friend class 클래스명;
- 여기서 만약 쌍방으로 friend로 되어 있지 않다면 friend를 요청받은 클래스만 정보를 공유하고 반대로 접근하는 것은 불가능하다.
한 쪽으로만 접근이 가능한 경우
class A {
private:
int x;
public:
friend class B;
};
class B {
private:
int y;
};
- 이 경우, B는 A의 private한 멤버 변수 x에 접근할 수 있지만, A는 B의 private한 멤버 변수 y에 접근할 수 없다.
양 쪽으로 접근이 가능한 경우
class A {
private:
int x;
public:
friend class B;
};
class B {
private:
int y;
public:
friend class A;
};
- 쌍방으로 friend를 설정하면, 각자의 클래스 멤버 변수에 접근할 수 있다.
friend 멤버 함수
- 특정 클래스의 멤버 함수만을 friend로 설정하여, 한정적으로 private한 정보에 접근할 수 있도록 한다.
- 이를 통해 정보 은닉 및 캡슐화가 강해진다.
class A {
private:
int x;
public:
friend void B::access(const A& x); // friend 멤버 함수 선언
};
class B {
private:
int y;
public:
void access(const A& x);
};
- 클래스 B의 멤버 함수인 access만이 클래스 A의 멤버 변수인 x에 접근할 수 있다.
friend 키워드 사용하기
- 각 객체에 숫자를 부여하여 응용하는 간단한 클래스를 구현한다.
friend 전역 함수
friend 키워드를 사용하지 않은 경우 (컴파일 오류 발생)
#include <iostream>
using namespace std;
class Example {
private:
int num;
public:
Example(int _num);
void printNum();
};
Example::Example(int _num) {
num = _num;
}
Example sum(const Example& a, const Example& b) { // 전역함수로 선언
Example temp(a.num + b.num);
return temp;
}
void Example::printNum() {
cout << num << endl;
}
int main() {
Example a(10);
Example b(20);
Example c = sum(a, b);
c.printNum();
return 0;
}
- Example temp(a.num, b.num)에서 a와 b의 멤버 변수가 private인 관계로 접근할 수 없다.
- 이때 다음과 같이 Sum 함수를 friend로 선언하여 클래스 내에 정의하면 private 멤버 변수에 접근할 수 있다.
friend Example sum(const Example& a, const Example& b);
friend 키워드를 사용한 경우 (컴파일 오류 X)
#include <iostream>
using namespace std;
class Example {
private:
int num;
public:
Example(int _num);
void printNum();
friend Example sum(const Example& a, const Example& b);
};
Example::Example(int _num) {
num = _num;
}
Example sum(const Example& a, const Example& b) {
Example temp(a.num + b.num);
return temp;
}
void Example::printNum() {
cout << num << endl;
}
int main() {
Example a(10);
Example b(20);
Example c = sum(a, b);
c.printNum();
return 0;
}
30
- 전역함수 sum을 friend 함수로 정의함으로써, a와 b의 private 멤버 변수 num을 합하여 객체 c를 생성할 수 있었다.
friend 클래스
#include <iostream>
using namespace std;
class Ex2; // 전방선언 -> Ex2가 Ex1의 멤버를 friend로 선언하기 이전에 미리 Ex1 멤버의 선언을 알아야 함
// 순환 참조를 피하기 위함
class Ex1 {
private:
int num;
public:
Ex1(int _num) { num = _num; }
void sum(const Ex2&);
void printNum();
};
class Ex2 {
private:
int num;
public:
Ex2(int _num) { num = _num; }
friend class Ex1; // friend 클래스 선언
};
void Ex1::sum(const Ex2& a) {
this->num += a.num; // Ex2 클래스의 private 멤버 변수에 접근
}
void Ex1::printNum() {
cout << this->num << endl;
}
int main() {
Ex1 ex1(10);
Ex2 ex2(20);
ex1.printNum();
ex1.sum(ex2);
ex1.printNum();
return 0;
}
10
30
- 순환 참조를 피하기 위해서 맨 앞에 Ex2 클래스의 원형을 미리 선언하였다.
- 클래스 Ex1에는 멤버 함수로 num 값을 더하는 sum 함수와 num을 출력하는 printNum 함수가 있다.
- 클래스 Ex2에 Ex1를 friend로 지정하여, Ex1는 클래스 Ex2의 private한 멤버 변수 num에 접근할 수 있다.
- 따라서 값이 10에서 30으로 변하였다.
friend 멤버 함수
#include <iostream>
using namespace std;
class Ex2; // 전방선언
class Ex1 {
private:
int num;
public:
Ex1(int _num) { num = _num; }
void sum(const Ex2&);
void printNum();
};
class Ex2 {
private:
int num;
public:
Ex2(int _num) { num = _num; }
friend void Ex1::sum(const Ex2&); // friend 멤버 함수 선언
};
void Ex1::sum(const Ex2& a) {
this->num += a.num; // Ex2 클래스의 private 멤버 변수에 접근
}
void Ex1::printNum() {
cout << this->num << endl;
}
int main() {
Ex1 ex1(10);
Ex2 ex2(20);
ex1.printNum();
ex1.sum(ex2);
ex1.printNum();
return 0;
}
10
30
- Ex1 클래스에 멤버 함수 sum을 구현하였고, 이를 Ex2에서 friend 멤버 함수로 선언하였다.
- 따라서 Ex1의 멤버 함수 sum을 통해서는 Ex2의 private 멤버 변수에 접근할 수 있다.
- ex1 객체에서 sum을 호출하여 ex2 객체의 num 값에 접근하였고, 이를 통해 값이 10에서 30으로 변하였다.