Menu
Coddy logo textTech

프렌드 함수와 클래스

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 32번째.

때때로 클래스 외부의 함수나 클래스가 해당 클래스의 private 멤버에 접근해야 할 때가 있습니다. 이러한 멤버들을 public으로 만드는 대신, C++은 캡슐화를 유지하면서 선택적인 접근 권한을 부여할 수 있도록 friend 키워드를 제공합니다.

프렌드 함수(friend function)는 클래스 내부에서 선언되지만 해당 클래스의 멤버는 아닙니다. 이 함수는 클래스의 private 및 protected 멤버에 접근할 수 있습니다:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // printWidth를 프렌드로 선언
    friend void printWidth(const Box& b);
};

// 프렌드 함수 정의 - 멤버 함수가 아님
void printWidth(const Box& b) {
    std::cout << b.width;    // private 멤버에 접근 가능
}

printWidth가 범위 지정 연산자 없이 클래스 외부에서 정의된 것을 주목하세요. 이것은 특별한 접근 권한을 가진 일반 함수입니다.

클래스 전체를 프렌드(friend)로 만들 수도 있으며, 이를 통해 해당 클래스의 모든 멤버 함수가 비공개(private) 멤버에 접근할 수 있도록 허용할 수 있습니다:

class Engine {
    int horsepower;
    
    friend class Mechanic;    // Mechanic은 private 멤버에 접근할 수 있습니다
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // 허용됨 - Mechanic은 friend입니다
    }
};

프렌드 관계는 상호적이지 않습니다. 만약 EngineMechanic을 프렌드로 선언한다면, MechanicEngine의 private 멤버에 접근할 수 있지만 그 반대는 불가능합니다. 프렌드는 클래스 간의 강력한 결합(tight coupling)을 생성하므로 가급적 드물게 사용하십시오. 프렌드는 연산자 오버로딩(operator overloading)이나 밀접하게 관련된 헬퍼 함수에 가장 유용합니다.

challenge icon

챌린지

쉬움

MessageReader 클래스가 SecureMessage 클래스에 저장된 암호화된 메시지를 읽기 위해 특별한 접근 권한이 필요한 보안 메시징 시스템을 구축해 봅시다. 이는 프렌드 함수(friend functions)와 프렌드 클래스(friend classes)를 사용하기에 완벽한 시나리오입니다.

코드를 정리하기 위해 두 개의 파일을 생성합니다:

  • SecureMessage.h: 비공개 메시지 데이터를 저장하고 신뢰할 수 있는 엔티티에 선택적 접근 권한을 부여하는 SecureMessage 클래스를 정의합니다. 클래스에는 다음이 포함되어야 합니다:
    • Private 멤버: content (string) 및 secretKey (int)
    • 내용(content)과 비밀 키(secret key)를 인자로 받는 생성자
    • 비공개 멤버에 접근할 수 있도록 MessageReader를 프렌드 클래스로 선언
    • 제공된 키가 비밀 키와 일치하는 경우에만 내용을 반환하고, 그렇지 않으면 "Access Denied"를 반환하는 프렌드 함수 decryptMessage(const SecureMessage& msg, int key)를 선언

    그런 다음 동일한 헤더 파일에 MessageReader 클래스를 정의합니다:

    • 메시지의 비공개 content에 직접 접근하여 반환하는 readContent(const SecureMessage& msg) 메서드
    • 제공된 키가 메시지의 secretKey와 일치하는지 확인하고 "Valid" 또는 "Invalid"를 반환하는 verifyKey(const SecureMessage& msg, int key) 메서드

    마지막으로, 클래스 정의 다음에 decryptMessage 프렌드 함수를 정의합니다.

  • main.cpp: 프렌드 함수와 프렌드 클래스가 실제로 작동하는 모습을 보여줍니다. 입력으로부터 메시지 내용, 비밀 키, 테스트 키를 각각 한 줄씩 읽은 후 다음을 수행합니다:
    • 내용과 비밀 키를 사용하여 SecureMessage 객체를 생성합니다.
    • MessageReader 객체를 생성합니다.
    • 리더의 readContent() 메서드를 사용하여 "Reader access: <content>"를 출력합니다.
    • 테스트 키와 함께 verifyKey()를 사용하여 "Key verification: <result>"를 출력합니다.
    • 테스트 키와 함께 프렌드 함수를 사용하여 "Decrypt with test key: <result>"를 출력합니다.
    • 원본 비밀 키와 함께 프렌드 함수를 사용하여 "Decrypt with correct key: <result>"를 출력합니다.

프렌드 함수는 범위 지정 연산자(scope resolution operator) 없이 클래스 외부에서 정의된다는 점을 기억하세요. 이들은 특별한 접근 권한을 가진 일반 함수입니다. MessageReader 클래스는 프렌드이므로 모든 메서드에서 SecureMessage의 비공개 멤버에 직접 접근할 수 있습니다.

std::stoi()를 사용하여 입력받은 키 문자열을 정수로 변환하세요. main.cpp에 #include "SecureMessage.h"를 사용하여 헤더 파일을 포함시키세요.

치트 시트

friend 키워드는 캡슐화를 유지하면서 private 멤버에 대한 선택적 접근을 허용합니다.

프렌드 함수 (Friend Function): 클래스 내부에서 선언되어 해당 클래스의 private 및 protected 멤버에 접근할 수 있지만, 클래스의 멤버는 아닌 함수입니다:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // 프렌드 함수 선언
    friend void printWidth(const Box& b);
};

// 범위 지정 연산자 없이 클래스 외부에서 정의
void printWidth(const Box& b) {
    std::cout << b.width;    // private 멤버에 접근 가능
}

프렌드 클래스 (Friend Class): 클래스 전체를 프렌드로 선언할 수 있으며, 이 경우 해당 클래스의 모든 멤버 함수가 private 멤버에 접근할 수 있게 됩니다:

class Engine {
    int horsepower;
    
    friend class Mechanic;    // 모든 Mechanic 메서드가 private 멤버에 접근할 수 있음
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // 허용됨
    }
};

주요 특징:

  • 프렌드 관계는 상호적이지 않습니다. A가 B를 프렌드로 선언하면 B는 A의 private 멤버에 접근할 수 있지만, 그 반대는 성립하지 않습니다.
  • 프렌드 함수는 범위 지정 연산자 없이 클래스 외부에서 정의됩니다.
  • 프렌드는 클래스 간의 강한 결합을 만들기 때문에 가급적 드물게 사용해야 합니다.
  • 연산자 오버로딩이나 밀접하게 관련된 헬퍼 함수에 가장 유용합니다.

직접 해보기

#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;

int main() {
    // 입력 읽기
    string messageContent;
    string secretKeyStr;
    string testKeyStr;
    
    getline(cin, messageContent);
    getline(cin, secretKeyStr);
    getline(cin, testKeyStr);
    
    int secretKey = stoi(secretKeyStr);
    int testKey = stoi(testKeyStr);
    
    // TODO: content와 secretKey를 사용하여 SecureMessage 생성
    
    // TODO: MessageReader 객체 생성
    
    // TODO: readContent()를 사용하여 "Reader access: <content>" 출력
    
    // TODO: testKey와 verifyKey()를 사용하여 "Key verification: <result>" 출력
    
    // TODO: testKey와 decryptMessage를 사용하여 "Decrypt with test key: <result>" 출력
    
    // TODO: secretKey와 decryptMessage를 사용하여 "Decrypt with correct key: <result>" 출력
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨