C ++에서 'friend'를 언제 사용해야합니까?


354

나는 C ++ FAQ를 읽고 friend선언 에 대해 궁금했다 . 나는 개인적으로 사용하지는 않았지만 언어 탐구에 관심이 있습니다.

사용하는 좋은 예는 무엇입니까 friend?


FAQ를 조금 더 읽으면 << >>연산자가 과부하되어 해당 클래스의 친구로 추가되는 아이디어가 마음에 듭니다 . 그러나 이것이 캡슐화를 어떻게 깨뜨리지 않는지 잘 모르겠습니다. 이러한 예외가 OOP 인 엄격 성 내에 언제 머물 수 있습니까?


5
나는 친구 클래스가 반드시 나쁜 것이 아니라는 대답에 동의하지만, 그것을 작은 코드로 취급하는 경향이 있습니다. 항상 그런 것은 아니지만 클래스 계층 구조를 다시 고려해야한다는 것을 나타내는 경우가 많습니다.
Mawg는 모니카

1
이미 타이트한 커플 링이있는 friend 클래스를 사용합니다. 그것이 만들어졌습니다. 예를 들어, 데이터베이스 테이블과 해당 인덱스는 밀접하게 연결되어 있습니다. 테이블이 변경되면 모든 인덱스를 업데이트해야합니다. 따라서 DBIndex 클래스는 DBTable이 인덱스 내부에 직접 액세스 할 수 있도록 DBTable을 친구로 선언합니다. 그러나 DBIndex에 대한 공용 인터페이스는 없습니다. 인덱스를 읽는 것조차 의미가 없습니다.
shawnhcorey

실질적인 경험이 거의없는 OOP "순수 자들"은 친구가 OOP 원칙을 위반한다고 주장합니다. 두 클래스가 공유 개인 상태를 유지해야하는 일반적인 상황이 발생할 때까지는 문제가 없습니다.
kaalus

답변:


335

먼저 (IMO)는 friend유용하지 않다고 말하는 사람들의 말을 듣지 않습니다. 쓸모있다. 많은 상황에서 공개적으로 사용할 수없는 데이터 또는 기능을 가진 개체가 있습니다. 이것은 다른 분야에만 표면적으로 만 익숙 할 수있는 많은 저자가있는 대규모 코드베이스에서 특히 그렇습니다.

프렌드 지정자에 대한 대안이 있지만, 종종 성가 시거나 (cpp 레벨의 구체적인 클래스 / 마스크 타입 정의), 완벽하지 않습니다 (설명 또는 함수 이름 규칙).

대답에;

friend지정자는 친구 문을 클래스 내에서 보호 된 데이터 나 기능에 지정된 클래스에 액세스 할 수 있습니다. 예를 들어 아래 코드에서 누구나 어린이에게 이름을 요청할 수 있지만 어머니와 어린이 만 이름을 변경할 수 있습니다.

Window와 같은 더 복잡한 클래스를 고려하여이 간단한 예를 더 살펴볼 수 있습니다. Window에는 공개적으로 액세스 할 수 없어야하지만 WindowManager와 같은 관련 클래스에 필요한 많은 기능 / 데이터 요소가있을 것입니다.

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};

114
추가로, C ++ FAQ는 캡슐화 를 friend 향상시킵니다 . 마찬가지로 회원 friend에게 선택적 액세스 권한 을 부여합니다 protected. 세분화 된 제어는 공개 액세스 권한을 부여하는 것보다 낫습니다. 다른 언어도 선택적 액세스 메커니즘을 정의 internal합니다. C #을 고려하십시오 . 사용에 대한 가장 부정적인 비판 friend은 타이트한 커플 링과 관련이 있으며 일반적으로 나쁜 것으로 보입니다. 그러나 어떤 경우에는 더 긴밀한 커플 링이 정확히 원하는 것이며 friend그 힘을 제공합니다.
André Caron

5
Andrew의 (cpp-level concrete classes)와 (masked typedefs)에 대해 더 말씀해 주 시겠습니까?
OmarOthman

18
이 답변은 동기부여하는 예를 friend제공하는 것이 아니라 무엇이 무엇인지 설명하는 데 더 집중되어있는 것 같습니다 . Window / WindowManager 예제는 표시된 예제보다 낫지 만 너무 모호합니다. 이 답변은 질문의 캡슐화 부분도 다루지 않습니다.
bames53

4
C ++에는 모든 멤버가 구현 세부 정보를 공유 할 수있는 패키지에 대한 개념이 없기 때문에 효과적으로 '친구'가 존재합니까? 나는 실제 사례에 정말로 관심이 있습니다.
weberc2

1
@OMGtechy C ++에 패키지 개념이 있다면 그렇게 할 필요가 없으므로 이전의 진술과 일치합니다. 친구 대신 패키지를 사용하여 개인 회원에게 액세스하는 Go의 예는 다음과 같습니다. play.golang.org/p/xnade4GBAL
weberc2

162

직장에서 우리는 광범위하게 코드를 테스트하기 위해 친구를 사용 합니다. 그것은 우리가 메인 어플리케이션 코드를 위해 적절한 캡슐화와 정보 숨기기를 제공 할 수 있다는 것을 의미합니다. 또한 친구를 사용하여 테스트 할 내부 상태 및 데이터를 검사하는 별도의 테스트 코드를 가질 수도 있습니다.

친구 키워드를 디자인의 필수 구성 요소로 사용하지 않을 것입니다.


그것이 바로 내가 사용하는 것입니다. 또는 멤버 변수를 protected로 설정하십시오. 그것은 C ++ / CLI :-(에 대한이 작동하지 않습니다 단지 수치
존 케이지

12
개인적으로 나는 이것을 권장하지 않습니다. 일반적으로 인터페이스를 테스트하는 중입니다. 즉, 일련의 입력이 예상되는 출력 집합을 제공합니다. 내부 데이터를 검사해야하는 이유는 무엇입니까?
Graeme

55
@Graeme : 좋은 테스트 계획에는 화이트 박스와 블랙 박스 테스트가 모두 포함되기 때문입니다.
벤 Voigt

1
이 답변 에서 완벽하게 설명 된 것처럼 @Graeme에 동의하는 경향 있습니다.
Alexis Leclerc

2
@Graeme 내부 데이터가 아닐 수도 있습니다. 나는 그 메소드가 클래스에 대해 비공개이며 공개적으로 액세스 할 수없는 데이터에서 특정 작업이나 작업을 수행하는 메소드 일 수 있지만 다른 객체는 해당 클래스의 보호 된 메소드에 자체 데이터를 제공하거나 시드해야 할 수도 있습니다.
Francis Cugler

93

friend키워드는 좋은 용도의 번호가 있습니다. 나에게 즉시 보이는 두 가지 용도는 다음과 같습니다.

친구 정의

친구 정의를 사용하면 클래스 범위에서 함수를 정의 할 수 있지만 함수는 멤버 함수로 정의되지 않고 둘러싸는 네임 스페이스의 무료 함수로 정의되며 인수 종속 조회를 제외하고는 정상적으로 표시되지 않습니다. 따라서 연산자 오버로드에 특히 유용합니다.

namespace utils {
    class f {
    private:
        typedef int int_type;
        int_type value;

    public:
        // let's assume it doesn't only need .value, but some
        // internal stuff.
        friend f operator+(f const& a, f const& b) {
            // name resolution finds names in class-scope. 
            // int_type is visible here.
            return f(a.value + b.value);
        }

        int getValue() const { return value; }
    };
}

int main() {
    utils::f a, b;
    std::cout << (a + b).getValue(); // valid
}

개인 CRTP 기본 클래스

때로는 정책에서 파생 클래스에 액세스해야 할 필요성이 있습니다.

// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
    void doSomething() {
        // casting this to Derived* requires us to see that we are a 
        // base-class of Derived.
        some_type const& t = static_cast<Derived*>(this)->getSomething();
    }
};

// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
    // we derive privately, so the base-class wouldn't notice that, 
    // (even though it's the base itself!), so we need a friend declaration
    // to make the base a friend of us.
    friend class SomePolicy<FlexibleClass>;

    void doStuff() {
         // calls doSomething of the policy
         this->doSomething();
    }

    // will return useful information
    some_type getSomething();
};

답변 에서 이에 대한 논증되지 않은 예를 찾을 수 있습니다. 그것을 사용하는 또 다른 코드는 답변에 있습니다. CRTP베이스는이 포인터를 캐스트하여 데이터 멤버 포인터를 사용하여 파생 클래스의 데이터 필드에 액세스 할 수 있습니다.


안녕하세요, CRTP를 시험해 볼 때 (xcode 4에서) 구문 오류가 발생합니다. Xcode는 클래스 템플릿을 상속하려고한다고 생각합니다. 오류가 발생 P<C>에서 template<template<typename> class P> class C : P<C> {};"클래스 템플릿 C의 사용은 템플릿 인수가 필요합니다"라는. 같은 문제가 있거나 해결책을 알고 있습니까?
bennedich

언뜻보기에 @bennedich, 그것은 당신이 불충분 한 C ++ 기능 지원으로 인한 일종의 오류처럼 보입니다. 컴파일러들 사이에서 매우 일반적입니다. FlexibleClasswithin 사용 FlexibleClass은 암시 적으로 자체 유형을 참조해야합니다.
Yakk-Adam Nevraumont

@bennedich : 클래스 본문 내에서 클래스 템플릿 이름 사용 규칙이 C ++ 11로 변경되었습니다. 컴파일러에서 C ++ 11 모드를 활성화하십시오.
벤 Voigt

Visual Studio 2015에서 다음을 추가하십시오. f () {}; f (int_type t) : 값 (t) {}; 이 컴파일러 오류를 방지하려면 오류 C2440 : '<function-style-cast>': 'utils :: f :: int_type'에서 'utils :: f'로 변환 할 수 없습니다. 참고 : 생성자는 소스 유형 또는 생성자를 사용할 수 없습니다. 과부하 해결은 애매했다
Damian

41

@roo : 클래스 자체가 개인 멤버에 액세스 할 수있는 사람을 지시하기 때문에 캡슐화는 여기서 중단되지 않습니다. 캡슐화는 클래스 외부에서 발생할 수있는 경우 (예 : operator <<"클래스의 친구입니다 foo."

friend사용 public하지 않고 사용합니다 private.

실제로 C ++ FAQ는 이미 이에 대한 답변을 제공합니다 .


14
"! 친구 대체합니다 공공의 사용이 아닌 개인의 사용"내가 두 번째 그
왈 리드 Eissa

26
@Assaf : 그렇습니다. 그러나 FQA는 대부분 실제 가치가없는 많은 일관된 성난 횡설수설입니다. 그 부분 friend도 예외는 아닙니다. 여기에서 유일한 실제 관찰은 C ++이 컴파일 타임에만 캡슐화를 보장한다는 것입니다. 그리고 더 이상 말을 할 필요가 없습니다. 나머지는 볼록입니다. 요약하자면, FQA의이 부분은 언급 할 가치가 없습니다.
Konrad Rudolph

12
그 FQA의 대부분은 : 완전한 BLX입니다
라마 - JKA toti

1
@Konrad : "여기서 실제로 관찰 할 수있는 것은 C ++이 컴파일 타임에만 캡슐화를 보장한다는 것입니다." 런타임에이를 보장하는 언어가 있습니까? 내가 아는 한 C #, Java, Python 및 기타 여러 언어에서 개인 구성원 (및 함수 또는 함수에 대한 포인터를 허용하는 언어의 경우)에 대한 참조를 반환하는 것이 허용됩니다.
André Caron

@ André : JVM과 CLR은 실제로 내가 아는 한 이것을 보장 할 수 있습니다 . 나는 그것이 항상 행해졌는지 모른다. 그러나 당신은 그러한 침입으로부터 패키지 / 어셈블리를 보호 할 수 있다고 주장 할 수있다.
Konrad Rudolph

27

일반적인 예는 연산자 <<를 오버로드하는 것입니다. 또 다른 일반적인 용도는 도우미 또는 관리자 클래스가 내부에 액세스하도록 허용하는 것입니다.

다음은 C ++ 친구에 대해 들었던 몇 가지 지침입니다. 마지막 것은 특히 기억에 남습니다.

  • 친구는 자녀의 친구가 아닙니다.
  • 자녀의 친구는 친구가 아닙니다.
  • 친구 만 개인 부품을 만질 수 있습니다.

" 정식 예 과부하 << 연산자이다.「사용하지 않는 표준 friendI 추측.
curiousguy

16

편집 : faq를 조금 더 읽습니다. 나는 << >> 연산자 오버로드와 그 클래스의 친구로 추가하는 아이디어를 좋아하지만 이것이 캡슐화를 어떻게 깨뜨리지 않는지 잘 모르겠습니다.

캡슐화가 어떻게 중단됩니까?

데이터 멤버에 대한 무제한 액세스를 허용하면 캡슐화가 중단됩니다 . 다음 클래스를 고려하십시오.

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

c1되는 분명히 캡슐화 없습니다. 누구나 읽고 읽을 수 있습니다 x. 우리는 어떤 종류의 액세스 제어를 시행 할 방법이 없습니다.

c2분명히 캡슐화되어 있습니다. 에 대한 공개 액세스 권한이 없습니다 x. 클래스에서 의미있는 작업foo 을 수행 하는 함수를 호출하기 만하면됩니다 .

c3? 덜 캡슐화되어 있습니까? 무제한 액세스를 허용합니까 x? 알 수없는 기능 액세스를 허용합니까?

아니요. 정확히 하나의 함수가 클래스의 개인 멤버에 액세스 할 수 있습니다 . 마찬가지로는 c2않았다. 마찬가지로 c2액세스 권한을 가진 하나의 함수는 "일부 임의의 알려지지 않은 함수"가 아니라 "클래스 정의에 나열된 함수"입니다. 마찬가지로 c2클래스 정의를 보면 액세스 권한이있는 사람 의 전체 목록을 볼 수 있습니다.

그렇다면 정확히 어떻게 덜 캡슐화됩니까? 같은 양의 코드가 클래스의 개인 멤버에게 액세스 할 수 있습니다. 그리고 모두 액세스 할 수있는 클래스 정의에 나열되어 있습니다.

friend캡슐화를 중단하지 않습니다. "OOP"라고 말하면 실제로 "Java"를 의미 하기 때문에 일부 Java 사용자 프로그래머는 불편 함을 느끼게 합니다. "캡슐화"라고 말하면 "비공개 멤버가 임의 액세스로부터 보호되어야 함"을 의미하는 것이 아니라 "비공개 멤버에 액세스 할 수있는 유일한 함수는 클래스 멤버"인 Java 클래스 입니다. 몇 가지 이유 .

먼저 이미 표시된 것처럼 너무 제한적입니다. 친구 방법으로 동일한 작업을 수행 할 수없는 이유는 없습니다.

둘째, 그것은 충분히 제한적이지 않다 . 네 번째 클래스를 고려하십시오.

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

앞서 언급 한 Java 사고 방식에 따르면 완벽하게 캡슐화되어 있습니다. 그럼에도 불구하고 절대적으로 누구나 x를 읽고 수정할 수 있습니다. 그게 어떻게 말이 되나요? (힌트 : 그렇지 않습니다)

결론 : 캡슐화는 개인 멤버에 액세스 할 수있는 기능을 제어 할 수있게하는 것입니다. 그것은 것입니다 하지 이러한 기능의 정의가있는 정확한 위치에 대한.


10

Andrew의 또 다른 일반적인 예인 공포스러운 코드 커플 링

parent.addChild(child);
child.setParent(parent);

두 행이 항상 함께 일관된 순서로 수행되는지 걱정하지 않고 메소드를 개인용으로 만들고 일관성을 강화하는 친구 기능을 가질 수 있습니다.

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

즉, 공용 인터페이스를 더 작게 유지하고 친구 기능의 클래스와 객체를 가로 지르는 불변을 적용 할 수 있습니다.


6
왜 누군가 친구가 필요합니까? addChild멤버 함수도 부모를 설정 하지 않습니까?
Nawaz

1
더 좋은 예는 만드는 것입니다 setParent당신이 그것을 관리 할 것이기 때문에 클라이언트가 부모를 변경할 수 있도록하지 않는 한, 친구 addChild/의 removeChild기능의 범주입니다.
Ylisar

8

개인 / 보호 / 공개 권한을 사용하여 구성원 및 기능에 대한 액세스 권한을 제어합니까? 이 3 가지 레벨 각각에 대한 아이디어가 명확하다고 가정하면 무언가 빠진 것이 분명합니다.

예를 들어 보호 된 멤버 / 함수 선언은 매우 일반적입니다. 당신은이 기능이 모든 사람에게 도달 할 수 없다고 말하고 있습니다 (물론 상속 된 아이는 제외). 그러나 예외는 어떻습니까? 모든 보안 시스템을 통해 어떤 종류의 '화이트리스트'를 가질 수 있습니까?

따라서 friend를 사용하면 견고한 개체를 유연하게 분리 할 수 ​​있지만 정당하다고 생각되는 것들에 대해 "루프 홀 (loophole)"을 만들 수 있습니다.

나는 그것이 없이는 항상 디자인이 있기 때문에 사람들이 필요하지 않다고 말합니다. 나는 그것이 글로벌 변수에 대한 토론과 비슷하다고 생각합니다. 당신은 절대로 사용해서는 안됩니다. 그것들 없이는 항상 할 수있는 방법이 있습니다 ...하지만 실제로는 (거의) 가장 우아한 방법 인 사례를 보게됩니다. .. 나는 이것이 친구와 같은 경우라고 생각합니다.

설정 기능을 사용하지 않고 멤버 변수에 액세스 할 수있는 것 외에는 실제로 잘하지 않습니다.

그것은 정확히 그것을 보는 방법이 아닙니다. 아이디어는 WHO가 설정 기능 이 있거나 관련이없는 것에 액세스 할 수 있도록 제어 하는 것입니다.


2
어떻게 friend허점은? 클래스에 나열된 메소드 가 개인 멤버에 액세스 할 수 있습니다 . 여전히 임의의 코드가 액세스 할 수는 없습니다. 따라서 공개 멤버 기능과 다르지 않습니다.
jalf

friend는 C ++에서 C # / Java 패키지 수준 액세스에 접근 할 수있는 한 가깝습니다. @jalf-팩토리 클래스와 같은 친구 클래스는 어떻습니까?
오우거 시편 33

1
@ 오거 : 그들 어때요? 당신은 여전히 그 클래스를 구체적으로 제공 하고 있으며 아무도 다른 클래스의 내부에 액세스 할 수 없습니다. 클래스를 망칠 수있는 임의의 알 수없는 코드에 대한 게이트를 열어 두는 것이 아닙니다.
jalf

8

친구 액세스를 사용하기에 편리한 장소를 찾았습니다 : 개인 기능의 Unittest.


그러나 공공 기능도 사용할 수 있습니까? 친구 액세스를 사용하면 어떤 이점이 있습니까?
Zheng Qu

@Maverobot 질문에 대해 자세히 설명해 주시겠습니까?
VladimirS

5

친구는 컨테이너를 만들 때 해당 클래스의 반복자를 구현하려고 할 때 편리합니다.


4

우리는 친구를 사용하여 적절한 영향을 미쳤던 이전에 근무했던 회사에서 흥미로운 문제가 발생했습니다. 프레임 워크 부서에서 커스텀 OS를 통해 기본 엔진 레벨 시스템을 만들었습니다. 내부적으로 우리는 클래스 구조를 가졌습니다 :

         Game
        /    \
 TwoPlayer  SinglePlayer

이 수업은 모두 프레임 워크의 일부였으며 팀에서 관리했습니다. 회사에서 제작 한 게임은 게임 어린이 중 하나에서 파생 된이 프레임 워크 위에 구축되었습니다. 문제는 게임에 SinglePlayer와 TwoPlayer가 액세스해야하는 다양한 것들에 대한 인터페이스가 있었지만 프레임 워크 클래스 외부에 노출하고 싶지 않다는 것입니다. 해결책은 해당 인터페이스를 비공개로 만들고 TwoPlayer 및 SinglePlayer가 우정을 통해 해당 인터페이스에 액세스 할 수 있도록하는 것입니다.

솔직히이 모든 문제는 시스템을보다 잘 구현함으로써 해결 될 수 있었지만 우리는 우리가 가진 것에 갇혀있었습니다.


4

짧은 대답은 실제로 캡슐화를 향상시킬친구를 사용 하는 것입니다. 가독성과 유용성 (오퍼레이터 << 및 >>이 표준 예제 임)을 향상시키는 것도 좋은 이유입니다.

캡슐화 개선의 예와 같이 다른 클래스의 내부와 함께 작동하도록 특별히 설계된 클래스 (테스트 클래스가 떠오를 것)가 좋은 후보입니다.


" << 및 >> 연산자는 표준 예입니다. "오히려 표준 카운터 예 입니다.
curiousguy

@curiousguy : 연산자 <<이며 >>일반적으로 멤버 대신 친구입니다. 멤버를 만들면 사용하기 어색해지기 때문입니다. 물론, 그 운영자가 개인 데이터에 액세스해야하는 경우에 대해 이야기하고 있습니다. 그렇지 않으면 우정은 쓸모가 없습니다.
Gorpik

" 그 멤버를 만드는 것은 사용하기가 불편하기 때문에. "분명히, 제작 operator<<operator>>값 클래스의 멤버가 아닌 비회원 또는 회원이 i|ostream원하는 구문을 제공 할 것이며, 나는 하지 를 제안. " 나는 그 운영자가 개인 데이터에 액세스해야하는 경우에 대해 이야기하고 있습니다. "나는 왜 입력 / 출력 운영자가 개인 멤버에 액세스해야하는지 잘 모르겠습니다.
curiousguy

4

C ++의 제작자는 캡슐화 원칙을 어 기지 않는다고 말하며 인용 할 것입니다.

"친구"가 캡슐화를 위반합니까? 아니 그렇지 않아. "친구"는 회원 자격과 마찬가지로 액세스 권한을 부여하기위한 명시 적 메커니즘입니다. 표준 준수 프로그램에서는 소스를 수정하지 않고 클래스에 대한 액세스 권한을 부여 할 수 없습니다.

분명하다 ...


@ curiousguy : 템플릿의 경우에도 마찬가지입니다.
Nawaz

@Nawaz Template 우정은 인정 될 수 있지만, 우정 부여 클래스를 수정하지 않고도 누구나 새로운 부분 또는 명시 적 전문화를 할 수 있습니다. 그러나 그렇게 할 때 ODR 위반에주의하십시오. 어쨌든 그렇게하지 마십시오.
curiousguy

3

다른 용도 : 친구 (+ 가상 상속)는 클래스에서 파생되는 것을 피하기 위해 사용될 수 있습니다 (일명 : "클래스를 비활성화 할 수있게하십시오") => 1 , 2

에서 2 :

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 

3

TDD를 여러 번 수행하기 위해 C ++에서 'friend'키워드를 사용했습니다.

친구가 내 모든 것을 알 수 있습니까?


업데이트 : Bjarne Stroustrup site 에서 "friend"키워드에 대한 귀중한 답변을 찾았습니다 .

"친구"는 회원 자격과 마찬가지로 액세스 권한을 부여하기위한 명시 적 메커니즘입니다.


3

friend키워드 를 사용하는시기와 장소에 대해 매우 신중해야 하며, 귀하와 마찬가지로 나는 거의 사용하지 않았습니다. 다음은 사용 friend및 대안 에 대한 참고 사항입니다 .

두 객체가 동일한 지 확인하기 위해 두 객체를 비교한다고 가정 해 봅시다. 다음 중 하나를 수행 할 수 있습니다.

  • 접근자를 사용하여 비교를 수행합니다 (모든 ivar를 확인하고 동등성을 결정).
  • 또는 모든 회원을 공개하여 직접 액세스 할 수 있습니다.

첫 번째 옵션의 문제점은 많은 접근자가 직접 변수 액세스보다 약간 느리고 읽기가 어렵고 번거로울 수 있다는 것입니다. 두 번째 접근 방식의 문제점은 캡슐화를 완전히 중단한다는 것입니다.

좋은 점은 클래스의 private 멤버에 여전히 액세스 할 수있는 외부 함수를 정의 할 수있는 것입니다. friend키워드로 이를 수행 할 수 있습니다 .

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

이 방법은 equal(Beer, Beer)현재에 직접 액세스 할 수 있습니다 ab의 개인 회원 (일 수있는 char *brand, float percentAlcohol등이 다소 인위적인 예를 들어, 당신은 빨리 적용 할 것입니다 friend가 과부하에 == operator있지만, 우리가 얻을 수 있습니다.

몇 가지 참고할 사항 :

  • A friend는 클래스의 멤버 함수가 아닙니다
  • 그것은 클래스의 개인 멤버들에게 특별한 접근을 가진 일반적인 기능입니다
  • 모든 접근 자와 뮤 테이터를 친구로 교체하지 마십시오 (모든 것을 만들 수도 있습니다 public).
  • 우정은 상호 적이 지 않다
  • 우정은 전 이적이지 않다
  • 우정은 물려받지 못한다
  • 또는 C ++ FAQ에서 설명하는 것처럼 "내가 내게 친구 액세스 권한을 부여한다고해서 자녀에게 나에게 액세스 권한이 자동으로 부여되지 않으며 친구에게 나에게 액세스 권한이 자동으로 부여되지 않으며 자동으로 나에게 액세스 권한도 부여되지 않습니다 "

나는 friends다른 방법으로하기가 훨씬 어려운 경우 에만 사용 합니다. 또 다른 예를 들어, 많은 벡터 수학 함수는 종종 생성 friends의 상호 운용성 때문에 Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4, 등 그리고 그것은 친구가 아니라 어디서나 접근을 사용할 필요가 그냥 너무 쉽게합니다. 지적한 바와 같이 (디버깅에 매우 유용), 아마도 연산자에 friend적용될 때 종종 유용 하지만 다음과 같이 사용할 수도 있습니다.<<>>==

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

내가 말했듯이, 나는 friend자주 사용하지 않지만, 지금은 그때마다 필요한 것입니다. 도움이 되었기를 바랍니다!


2

operator << 및 operator >>와 관련하여 이러한 연산자를 친구로 만들만한 충분한 이유가 없습니다. 그들이 회원 기능이되어서는 안되지만, 친구 일 필요는 없습니다.

가장 좋은 방법은 공개 인쇄 (ostream &) 및 읽기 (istream &) 기능을 만드는 것입니다. 그런 다음 해당 기능의 관점에서 operator << 및 operator >>를 작성하십시오. 이를 통해 해당 기능을 가상으로 만들 수있어 가상 직렬화가 가능하다는 이점이 있습니다.


" 연산자 << 및 연산자 >>와 관련하여 이러한 연산자를 친구로 만들만한 충분한 이유가 없습니다. "절대적으로 맞습니다. " 이 기능을 가상으로 만들 수 있다는 추가적인 이점이 있습니다. "해당 클래스가 파생을 목적으로하는 경우 가능합니다. 그렇지 않으면 왜 귀찮게합니까?
curiousguy

이 답변이 왜 두 번이나 다운되었는지, 설명조차하지 않는 이유는 정말로 모르겠습니다! 그건 무례 해.
curiousguy

virtual은 직렬화에서 상당히 큰 성능을 발휘할 것입니다.
paulm

2

보호 된 기능을 단위 테스트하기 위해 친구 키워드 만 사용하고 있습니다. 보호 기능을 테스트해서는 안된다고 말하는 사람들도 있습니다. 그러나 새로운 기능을 추가 할 때이 유용한 도구를 찾으십시오.

그러나 클래스 선언에서 키워드를 직접 사용하지 않고 멋진 템플릿 해킹을 사용하여이를 달성합니다.

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

이를 통해 다음을 수행 할 수 있습니다.

friendMe(this, someClassInstance).someProtectedFunction();

GCC 및 MSVC에서 작동합니다.


2

C ++에서 "friend"키워드는 연산자 오버로드 및 브리지 만들기에 유용합니다.

1.) 연산자 오버로딩의 friend 키워드 : 연산자 오버로딩의
예는 다음과 같습니다. 두 개의 float 변수
"x"(x- 좌표)와 "y"(y- 좌표) 를 갖는 "Point"클래스가 있다고 가정 해 봅시다 . 이제 우리가 "<<"호출하면 "cout << pointobj"x와 y 좌표가 출력되도록 point (extract operator) 를 오버로드해야합니다 ( pointobj는 Point 클래스의 객체입니다). 이를 위해 두 가지 옵션이 있습니다.

   1. "ostream"클래스에서 "operator << ()"함수를 오버로드하십시오.
   2. "Point"클래스에서 "operator << ()"함수를 오버로드하십시오.
다른 클래스에 대해이 연산자를 다시 오버로드해야하는 경우 "ostream"클래스를 다시 변경해야하기 때문에 첫 번째 옵션은 좋지 않습니다.
이것이 두 번째가 최선의 선택 인 이유입니다. 이제 컴파일러는 "operator <<()"함수 를 호출 할 수 있습니다 .

   1. ostream 객체 cout 사용 : cout.operator << (Pointobj) (양식 ostream 클래스) 
2. 객체없이 호출합니다 .As : operator << (cout, Pointobj)

Beacause는 Point 클래스에서 오버로드를 구현했습니다. 따라서 객체없이이 함수를 호출하려면 객체 "friend"없이 친구 함수를 호출 할 수 있으므로 키워드 를 추가해야 합니다. 이제 함수 선언은 다음과 같습니다.
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.) bridge 만들기의 Friend 키워드 :
둘 이상의 클래스 (일반적으로 "bridge"라고 함)의 개인 멤버에 액세스해야하는 함수를 만들어야한다고 가정합니다. 방법 :
클래스의 비공개 멤버에 액세스하려면 해당 클래스의 멤버 여야합니다. 이제 다른 클래스의 비공개 멤버에 액세스하려면 모든 클래스가 해당 함수를 친구 함수로 선언해야합니다. 예를 들어 : 클래스 A와 B가 두 개 있다고 가정합니다. 함수는 두 클래스의 "funcBridge()"개인 멤버에 액세스하려고합니다. 그런 다음 두 클래스 모두 다음 "funcBridge()"과 같이 선언해야합니다 .
friend return_type funcBridge(A &a_obj, B & b_obj);

이것이 친구 키워드를 이해하는 데 도움이 될 것이라고 생각합니다.


2

친구 선언에 대한 참조 는 다음과 같이 말합니다.

친구 선언은 클래스 본문에 나타나고 친구 선언이 나타나는 클래스의 비공개 및 보호 된 멤버 에게 함수 또는 다른 클래스 액세스 권한을 부여합니다 .

따라서 일부 답변에는 보호 대상 회원 friend만 방문 할 수 있다는 기술적 인 오류가 있습니다 .


1

트리 예제는 꽤 좋은 예입니다. 상속 관계없이 몇 가지 다른 클래스에서 객체를 구현하는 것입니다.

어쩌면 생성자를 보호하고 사람들이 "친구"팩토리를 사용하도록해야 할 수도 있습니다.

... 좋아, 솔직히 당신은 그것 없이도 살 수 있습니다.


1

TDD를 여러 번 수행하기 위해 C ++에서 'friend'키워드를 사용했습니다.
친구가 내 모든 것을 알 수 있습니까?

아니, 유일한 편도 우정 :`(


1

내가 사용하는 특정 인스턴스 friendSingleton 클래스를 만들 때 입니다. friend키워드는 나에게 항상 클래스에 "하는 GetInstance ()"방법을 것보다 더 간결 접근 함수를 만들 수 있습니다.

/////////////////////////
// Header file
class MySingleton
{
private:
    // Private c-tor for Singleton pattern
    MySingleton() {}

    friend MySingleton& GetMySingleton();
}

// Accessor function - less verbose than having a "GetInstance()"
//   static function on the class
MySingleton& GetMySingleton();


/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
    static MySingleton theInstance;
    return theInstance;
}

이것은 맛의 문제 일지 모르지만, 몇 번의 키 스트로크 저장이 친구의 사용을 정당화한다고 생각하지 않습니다. GetMySingleton ()은 클래스의 정적 메소드 여야합니다.
Gorpik

비공개 c-tor는 친구가 아닌 기능이 MySingleton을 인스턴스화하는 것을 허용하지 않으므로 여기에 친구 키워드가 필요합니다.
JBR 윌킨슨

@Gorpik " 이것은 맛의 문제 일지 모르지만, 몇 번의 키 스트로크 저장이 친구의 사용을 정당화한다고 생각하지 않습니다. " 어쨌든, friend않습니다 하지 특정 "정당성"을 필요로 멤버 함수를 추가 할 때하는 것은하지 않습니다.
curiousguy

싱글 톤은 나쁜 어쨌든 연습 "유해 싱글"(구글 고려하면 같은 결과를 많이 얻을 것이다 내가 그 기능을 잘 사용으로 간주 할 수있는 안티 패턴을 구현하는 기능을 사용하여 생각하지 않습니다..
weberc2

1

친구 기능 및 클래스는 일반적인 경우 캡슐화가 깨지지 않도록 개인 및 보호 된 클래스 멤버에게 직접 액세스 할 수 있습니다. 대부분의 사용법은 ostream과 함께 사용됩니다. 다음과 같이 입력 할 수 있습니다.

Point p;
cout << p;

그러나이 경우 Point의 개인 데이터에 액세스해야 할 수 있으므로 오버로드 된 연산자를 정의합니다

friend ostream& operator<<(ostream& output, const Point& p);

그러나 캡슐화에 대한 의미는 분명합니다. 먼저, 친구 클래스 또는 함수는 클래스의 모든 멤버, 심지어 필요와 관련이없는 멤버에 대한 모든 액세스 권한을 갖습니다. 둘째, 클래스와 친구의 구현은 이제 클래스의 내부 변경으로 인해 친구를 깨뜨릴 수있는 지점에 포함됩니다.

친구를 수업의 확장으로 보아도 논리적으로 말하면 문제가 아닙니다. 그러나 그 경우, 왜 친구를 먼저 창 밖으로 내쫓아 야합니까?

'친구'가 달성하려는 것과 똑같은 것을 달성하기 위해 캡슐화를 깨지 않고 다음과 같이 할 수 있습니다.

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

캡슐화가 깨지지 않고 클래스 B는 A의 내부 구현에 액세스 할 수 없지만 결과는 B를 A의 친구로 선언 한 것과 같습니다. 컴파일러는 함수 호출을 최적화하여 결과가 동일합니다. 직접 액세스 지침.

'친구'를 사용하는 것은 논쟁의 여지가 있지만 확실한 비용이 드는 지름길이라고 생각합니다.


1

이것은 실제 사용 사례 상황이 아닐 수도 있지만 클래스 간 친구 사용을 설명하는 데 도움이 될 수 있습니다.

클럽 하우스

class ClubHouse {
public:
    friend class VIPMember; // VIP Members Have Full Access To Class
private:
    unsigned nonMembers_;
    unsigned paidMembers_;
    unsigned vipMembers;

    std::vector<Member> members_;
public:
    ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}

    addMember( const Member& member ) { // ...code }   
    void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
    Amenity getAmenity( unsigned memberID ) { // ...code }

protected:
    void joinVIPEvent( unsigned memberID ) { // ...code }

}; // ClubHouse

멤버 클래스

class Member {
public:
    enum MemberShipType {
        NON_MEMBER_PAID_EVENT,   // Single Event Paid (At Door)
        PAID_MEMBERSHIP,         // Monthly - Yearly Subscription
        VIP_MEMBERSHIP,          // Highest Possible Membership
    }; // MemberShipType

protected:
    MemberShipType type_;
    unsigned id_;
    Amenity amenity_;
public:
    Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
    virtual ~Member(){}
    unsigned getId() const { return id_; }
    MemberShipType getType() const { return type_; }
    virtual void getAmenityFromClubHouse() = 0       
};

class NonMember : public Member {
public:
   explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}   

   void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class PaidMember : public Member {
public:
    explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class VIPMember : public Member {
public:
    friend class ClubHouse;
public:
    explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }

    void attendVIPEvent() {
        ClubHouse::joinVIPEvent( this->id );
    }
};

예의

class Amenity{};

여기에서이 클래스들의 관계를 살펴보면; ClubHouse는 다양한 유형의 회원 및 회원 액세스 권한을 보유하고 있습니다. 멤버는 모두 공통 클래스와 외부 클래스가 기본 클래스에있는 액세스 함수를 통해 자신의 ID 및 유형에 액세스 할 수있는 ID 및 공유 유형을 공유하므로 수퍼 또는 기본 클래스에서 파생됩니다.

그러나 이러한 종류의 멤버 및 파생 클래스 계층과 ClubHouse 클래스와의 관계를 통해 "특별한 권한"을 가진 파생 클래스 중 하나만이 VIPMember 클래스입니다. 기본 클래스와 다른 2 개의 파생 클래스는 ClubHouse의 joinVIPEvent () 메서드에 액세스 할 수 없지만 VIP 멤버 클래스는 해당 이벤트에 대한 전체 액세스 권한이있는 것처럼 해당 권한을 갖습니다.

VIPMember와 ClubHouse는 다른 멤버 클래스가 제한되어있는 양방향 액세스 거리입니다.


0

클래스에 트리 알고리즘을 구현할 때, 교수가 우리에게 준 프레임 워크 코드는 노드 클래스의 친구로서 트리 클래스를 가졌습니다.

설정 기능을 사용하지 않고 멤버 변수에 액세스 할 수있는 것 외에는 실제로 아무런 효과가 없습니다.


0

다른 클래스 (서로 상속되지 않음)가 다른 클래스의 개인 또는 보호 멤버를 사용하는 경우 우정을 사용할 수 있습니다.

친구 기능의 일반적인 사용 사례는 개인 또는 보호 된 구성원 모두에 액세스하는 서로 다른 두 클래스 사이에서 수행되는 작업입니다.

에서 http://www.cplusplus.com/doc/tutorial/inheritance/ .

비 멤버 메소드가 클래스의 프라이빗 멤버에 액세스하는이 예제를 볼 수 있습니다. 이 메소드는이 클래스에서 클래스의 친구로 선언되어야합니다.

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

0

아마도 위의 답변에서 무언가를 놓쳤지만 캡슐화의 또 다른 중요한 개념은 구현을 숨기는 것입니다. 개인 데이터 멤버 (클래스의 구현 세부 사항)에 대한 액세스를 줄이면 나중에 코드를 훨씬 쉽게 수정할 수 있습니다. 친구가 개인 데이터에 직접 액세스하는 경우 구현 데이터 필드 (개인 데이터)를 변경하면 해당 데이터에 액세스하는 코드가 손상됩니다. 액세스 방법을 사용하면 대부분이 문제가 해결됩니다. 상당히 중요하다고 생각합니다.


-1

당신은 할 수 엄격한과 순수한 OOP 원칙을 준수하고 모든 클래스에 대한 데이터 멤버도이 없도록 접근을 모든 개체 수 있도록 해야한다 간접 통해 그들에 행동 할 수있는 유일한 방법으로 자신의 데이터에 대해 알 수있는 유일한 사람이 될 메시지 , 즉, 방법.

그러나 C #에도 내부 가시성 키워드가 있으며 Java에는 기본적으로 패키지 수준의 액세스 가능성이 있습니다. C ++은 실제로 다른 클래스와 다른 클래스 볼 수 있는 클래스를 정확하게 지정하여 클래스에 대한 가시성의 타협을 minimizinbg가 이상적인 OOP에 더 가깝 습니다.

나는 실제로 C ++을 사용하지 않지만 C #에 friend 가 있으면 어셈블리 전역 내부 수정 자 대신 실제로 많이 사용합니다. .NET에서 배포 장치가 있기 때문에 정말, incapsulation을 아프게하지 않는 것입니다 어셈블리.

그러나 Cross-Assembly Friend 메커니즘 처럼 작동 하는 InternalsVisibleTo Attribute (otherAssembly)가 있습니다. Microsoft는이를 시각적 디자이너 어셈블리에 사용합니다.


-1

친구는 콜백에도 유용합니다. 콜백을 정적 메소드로 구현할 수 있습니다

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

내부 에서 callback호출 localCallback하고 clientData인스턴스에 인스턴스가 있습니다. 제 생각에는

또는...

class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}

이것이 허용하는 것은 친구가 cpp에서 c 스타일 함수로 순수하게 정의되고 클래스를 어지럽히 지 않는 것입니다.

마찬가지로, 내가 자주 본 적이 패턴이 모두 넣어하는 것입니다 정말 , 헤더에 선언 된 cpp에있는 정의와 friended했다 또 다른 클래스로 클래스의 private 멤버를. 이를 통해 코더는 클래스의 복잡성과 내부 작업을 헤더 사용자에게 숨길 수 있습니다.

헤더에서 :

class MyFooPrivate;
class MyFoo
{
    friend class MyFooPrivate;
public:
    MyFoo();
    // Public stuff
private:
    MyFooPrivate _private;
    // Other private members as needed
};

cpp에서

class MyFooPrivate
{
public:
   MyFoo *owner;
   // Your complexity here
};

MyFoo::MyFoo()
{
    this->_private->owner = this;
}

다운 스트림에서 볼 수없는 것을 숨기는 것이 더 쉬워집니다.


1
인터페이스가 이것을 달성하는 더 깨끗한 방법이 아닐까요? 누군가 MyFooPrivate.h를 찾는 것을 중지시키는 것은 무엇입니까?
JBR 윌킨슨

1
비밀을 지키기 위해 개인 및 공공을 사용하는 경우 쉽게 패배하게됩니다. "숨김"은 MyFoo 사용자가 실제로 개인 회원을 볼 필요가 없다는 것을 의미합니다. 이 외에도 ABI 호환성을 유지하는 것이 유용합니다. _private를 포인터로 만들면 공개 인터페이스를 건드리지 않고 개인 구현이 원하는만큼 변경되어 ABI 호환성을 유지할 수 있습니다.
shash

PIMPL 관용구를 언급하고 있습니다. 당신이 말하는 것처럼 추가 캡슐화가 아닌 목적은 구현 세부 사항을 변경하여 클라이언트 코드를 다시 컴파일하도록 강제하지 않습니다. 또한이 관용구를 구현하기 위해 친구를 사용할 필요가 없습니다.
weberc2

그래 주요 목적은 구현 세부 사항을 이동하는 것입니다. 친구는 공개 클래스 내에서 비공개 또는 다른 방식으로 비공개 멤버를 처리하는 데 유용합니다.
shash
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.