C ++에서 ‘friend’를 언제 사용해야합니까? << >>연산자가 과부하되어 해당 클래스의 친구로 추가되는

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

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


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



답변

먼저 (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 );
};

답변

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

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


답변

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베이스는이 포인터를 캐스트하여 데이터 멤버 포인터를 사용하여 파생 클래스의 데이터 필드에 액세스 할 수 있습니다.


답변

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

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

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


답변

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

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

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

답변

편집 : 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를 읽고 수정할 수 있습니다. 그게 어떻게 말이 되나요? (힌트 : 그렇지 않습니다)

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


답변

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);
}

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