[C/C++] Friend

할랑말랑·2026년 3월 9일

C/C++

목록 보기
12/45

Friend

C++ 객체 지향 프로그래밍의 핵심 원칙 중 하나인 캡슐화를 의도적으로 깨뜨리는, 아주 특별하고 강력한 키워드

friend로 선언된 외부의 함수나 클래스는, 해당 클래스의 private 및 protected 멤버에 직접 접근할 수 있는 권한을 얻게 됩니다.

friend 키워드가 가능한 대상

1. 전역 함수(Standalone Function)

#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가 되서 멤버에 접근할수있다.

2. 다른 클래스 전체(Another Class)

#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 클래스의 모든 멤버에 접근 할수있다.
이 방법은 매우 강력한 만큼, 두 클래스가 정말로 밀접하게 연관되어 하나의 개념처럼 동작할 때만 신중하게 사용해야한다. 남용하면 캡슐화가 완전히 무너진다.

3. 다른 클래스의 특정 맴버 함수

#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선언 가능
이것이 캡슐화를 가장 적게 해치는 방법

4. friend의 특징과 규칙

  • A가 B 를 friend 선언했다고 해서 B가 A를 자동으로 frined로 생각하지 않는다. 필요하면 B도 A를 friend 선언해야한다.
  • 자식 클래스는 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;
}

0개의 댓글