C++ 객체 지향 프로그래밍의 핵심 원칙 중 하나인 캡슐화를 의도적으로 깨뜨리는, 아주 특별하고 강력한 키워드
friend로 선언된 외부의 함수나 클래스는, 해당 클래스의 private 및 protected 멤버에 직접 접근할 수 있는 권한을 얻게 됩니다.
#include <iostream> #include <string> using namespace std; class Person { private: std::string name; int age; public: Person(const std::string& name, int age) : name(name), age(age) {} // 전역 함수 friend 선언 friend void printPerson(const Person& p); }; // Person 클래스의 friend 된 전역 함수 void printPerson(const Person& p) { // 이제 printPerson은 Person의 private 멤버에 직접 접근 가능! cout << "이름: " << p.name << ", 나이: " << p.age << endl; } int main() { Person p("홍길동", 30); printPerson(p); return 0; }friend 선언이 없었다면 PrintPerson 함수는 p.name,p.age에 접근할 수 없어 컴파일 에러가 발생하지만 friend 선언으로 해당 전역함수는 Person 클래스의 friend가 되서 멤버에 접근할수있다.
#include <iostream> #include <string> using namespace std; class Wallet { private: int money; public: Wallet() : money(10000) {} // Person 클래스 friend 선언 friend class Person; }; class Person { public: void spendMoney(Wallet& w, int amount) { // Person은 Wallet의 friend이므로 Wallet의 private 멤버인 money에 직접 접근 가능 if (w.money >= amount) { w.money -= amount; std::cout << amount << "원 사용. 남은 돈: " << w.money << "원" << std::endl; } else { std::cout << "돈이 부족합니다." << std::endl; } } }; int main() { Person p; Wallet w; p.spendMoney(w, 3000); // 3000원 사용. 남은 돈: 7000원 return 0; }Person 클래스는 Wallet 클래스의 모든 멤버에 접근 할수있다.
이 방법은 매우 강력한 만큼, 두 클래스가 정말로 밀접하게 연관되어 하나의 개념처럼 동작할 때만 신중하게 사용해야한다. 남용하면 캡슐화가 완전히 무너진다.
#include <iostream> #include <string> using namespace std; class TV; class Remote { public: void setChannel(TV& tv, int channel); }; class TV { private: int currentChannel; public: TV() : currentChannel(1) {} // Remote 클래스의 'setChannel' 함수만 friend 선언 friend void Remote::setChannel(TV& tv, int channel); void showChannel() { std::cout << "현재 채널: " << currentChannel << std::endl; } }; // setChannel 함수를 정의 void Remote::setChannel(TV& tv, int channel) { // 이 함수는 TV 클래스의 friend이므로 private 멤버에 접근 가능 tv.currentChannel = channel; } int main() { TV myTV; Remote myRemote; myTV.showChannel(); // 현재 채널: 1 myRemote.setChannel(myTV, 11); myTV.showChannel(); // 현재 채널: 11 return 0; }클래스가 아닌 특정 클래스의 특정 맴버 함수 하나만 friend선언 가능
이것이 캡슐화를 가장 적게 해치는 방법
#include <iostream> #include <string> using namespace std; class Point { private: int x; int y; public: Point(int x, int y) : x(x), y(y) {} // Point 클래스의 출력 연산자 오버로딩 friend ostream& operator<<(ostream& os, const Point& p) { os << "(" << p.x << ", " << p.y << ")"; return os; } }; int main() { Point p1(10, 20); cout << p1 << endl; // 이 코드가 (10, 20) 처럼 출력되기를 원함! return 0; }
- C++에서 멤버 함수를 통한 연산자 오버로딩은 항상 객체 자신이 왼쪽 피연산자가 된다. 원하는 경우는 왼쪽 피연산자가 C++ 표준 라이브러리에 이미 존재하는 ostream 타입의 cout 객체이다.
( p1 << cout ; 으로 원했던 cout << p1; 으로 할수가 없다.)operator<<를 멤버 함수가 아닌 전역 함수로 만들면 해결된다.
전역 함수로 만들면 클래스 private 멤버 변수를 접근 할수가 없다 그래서 friend 선언으로 문제를 해결한다.
#include <iostream> #include <string> using namespace std; class Point { private: int x; int y; public: Point(int x, int y) : x(x), y(y) {} friend ostream& operator<<(ostream& os, const Point& p); }; ostream& operator<<(ostream& os, const Point& p) { // os는 cout 객체를 참조 // p는 출력할 Point 객체를 참조 os << "(" << p.x << ", " << p.y << ")"; // ostream 객체를 다시 반환하여 cout << p1 << p2; 와 같은 // 연쇄적인 출력이 가능하게 함 return os; } int main() { Point p1(10, 20); cout << p1 << endl; return 0; }