나는 C ++ FAQ를 읽고 friend
선언 에 대해 궁금했다 . 나는 개인적으로 사용하지는 않았지만 언어 탐구에 관심이 있습니다.
사용하는 좋은 예는 무엇입니까 friend
?
FAQ를 조금 더 읽으면 <<
>>
연산자가 과부하되어 해당 클래스의 친구로 추가되는 아이디어가 마음에 듭니다 . 그러나 이것이 캡슐화를 어떻게 깨뜨리지 않는지 잘 모르겠습니다. 이러한 예외가 OOP 인 엄격 성 내에 언제 머물 수 있습니까?
나는 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
향상시킵니다 . 마찬가지로 회원 friend
에게 선택적 액세스 권한 을 부여합니다 protected
. 세분화 된 제어는 공개 액세스 권한을 부여하는 것보다 낫습니다. 다른 언어도 선택적 액세스 메커니즘을 정의 internal
합니다. C #을 고려하십시오 . 사용에 대한 가장 부정적인 비판 friend
은 타이트한 커플 링과 관련이 있으며 일반적으로 나쁜 것으로 보입니다. 그러나 어떤 경우에는 더 긴밀한 커플 링이 정확히 원하는 것이며 friend
그 힘을 제공합니다.
friend
제공하는 것이 아니라 무엇이 무엇인지 설명하는 데 더 집중되어있는 것 같습니다 . Window / WindowManager 예제는 표시된 예제보다 낫지 만 너무 모호합니다. 이 답변은 질문의 캡슐화 부분도 다루지 않습니다.
직장에서 우리는 광범위하게 코드를 테스트하기 위해 친구를 사용 합니다. 그것은 우리가 메인 어플리케이션 코드를 위해 적절한 캡슐화와 정보 숨기기를 제공 할 수 있다는 것을 의미합니다. 또한 친구를 사용하여 테스트 할 내부 상태 및 데이터를 검사하는 별도의 테스트 코드를 가질 수도 있습니다.
친구 키워드를 디자인의 필수 구성 요소로 사용하지 않을 것입니다.
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
}
때로는 정책에서 파생 클래스에 액세스해야 할 필요성이 있습니다.
// 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베이스는이 포인터를 캐스트하여 데이터 멤버 포인터를 사용하여 파생 클래스의 데이터 필드에 액세스 할 수 있습니다.
P<C>
에서 template<template<typename> class P> class C : P<C> {};
"클래스 템플릿 C의 사용은 템플릿 인수가 필요합니다"라는. 같은 문제가 있거나 해결책을 알고 있습니까?
FlexibleClass
within 사용 FlexibleClass
은 암시 적으로 자체 유형을 참조해야합니다.
@roo : 클래스 자체가 개인 멤버에 액세스 할 수있는 사람을 지시하기 때문에 캡슐화는 여기서 중단되지 않습니다. 캡슐화는 클래스 외부에서 발생할 수있는 경우 (예 : operator <<
"클래스의 친구입니다 foo
."
friend
사용 public
하지 않고 사용합니다 private
.
실제로 C ++ FAQ는 이미 이에 대한 답변을 제공합니다 .
friend
도 예외는 아닙니다. 여기에서 유일한 실제 관찰은 C ++이 컴파일 타임에만 캡슐화를 보장한다는 것입니다. 그리고 더 이상 말을 할 필요가 없습니다. 나머지는 볼록입니다. 요약하자면, FQA의이 부분은 언급 할 가치가 없습니다.
일반적인 예는 연산자 <<를 오버로드하는 것입니다. 또 다른 일반적인 용도는 도우미 또는 관리자 클래스가 내부에 액세스하도록 허용하는 것입니다.
다음은 C ++ 친구에 대해 들었던 몇 가지 지침입니다. 마지막 것은 특히 기억에 남습니다.
friend
I 추측.
편집 : 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);
}
즉, 공용 인터페이스를 더 작게 유지하고 친구 기능의 클래스와 객체를 가로 지르는 불변을 적용 할 수 있습니다.
addChild
멤버 함수도 부모를 설정 하지 않습니까?
setParent
당신이 그것을 관리 할 것이기 때문에 클라이언트가 부모를 변경할 수 있도록하지 않는 한, 친구 addChild
/의 removeChild
기능의 범주입니다.
개인 / 보호 / 공개 권한을 사용하여 구성원 및 기능에 대한 액세스 권한을 제어합니까? 이 3 가지 레벨 각각에 대한 아이디어가 명확하다고 가정하면 무언가 빠진 것이 분명합니다.
예를 들어 보호 된 멤버 / 함수 선언은 매우 일반적입니다. 당신은이 기능이 모든 사람에게 도달 할 수 없다고 말하고 있습니다 (물론 상속 된 아이는 제외). 그러나 예외는 어떻습니까? 모든 보안 시스템을 통해 어떤 종류의 '화이트리스트'를 가질 수 있습니까?
따라서 friend를 사용하면 견고한 개체를 유연하게 분리 할 수 있지만 정당하다고 생각되는 것들에 대해 "루프 홀 (loophole)"을 만들 수 있습니다.
나는 그것이 없이는 항상 디자인이 있기 때문에 사람들이 필요하지 않다고 말합니다. 나는 그것이 글로벌 변수에 대한 토론과 비슷하다고 생각합니다. 당신은 절대로 사용해서는 안됩니다. 그것들 없이는 항상 할 수있는 방법이 있습니다 ...하지만 실제로는 (거의) 가장 우아한 방법 인 사례를 보게됩니다. .. 나는 이것이 친구와 같은 경우라고 생각합니다.
설정 기능을 사용하지 않고 멤버 변수에 액세스 할 수있는 것 외에는 실제로 잘하지 않습니다.
그것은 정확히 그것을 보는 방법이 아닙니다. 아이디어는 WHO가 설정 기능 이 있거나 관련이없는 것에 액세스 할 수 있도록 제어 하는 것입니다.
friend
허점은? 클래스에 나열된 메소드 가 개인 멤버에 액세스 할 수 있습니다 . 여전히 임의의 코드가 액세스 할 수는 없습니다. 따라서 공개 멤버 기능과 다르지 않습니다.
우리는 친구를 사용하여 적절한 영향을 미쳤던 이전에 근무했던 회사에서 흥미로운 문제가 발생했습니다. 프레임 워크 부서에서 커스텀 OS를 통해 기본 엔진 레벨 시스템을 만들었습니다. 내부적으로 우리는 클래스 구조를 가졌습니다 :
Game
/ \
TwoPlayer SinglePlayer
이 수업은 모두 프레임 워크의 일부였으며 팀에서 관리했습니다. 회사에서 제작 한 게임은 게임 어린이 중 하나에서 파생 된이 프레임 워크 위에 구축되었습니다. 문제는 게임에 SinglePlayer와 TwoPlayer가 액세스해야하는 다양한 것들에 대한 인터페이스가 있었지만 프레임 워크 클래스 외부에 노출하고 싶지 않다는 것입니다. 해결책은 해당 인터페이스를 비공개로 만들고 TwoPlayer 및 SinglePlayer가 우정을 통해 해당 인터페이스에 액세스 할 수 있도록하는 것입니다.
솔직히이 모든 문제는 시스템을보다 잘 구현함으로써 해결 될 수 있었지만 우리는 우리가 가진 것에 갇혀있었습니다.
짧은 대답은 실제로 캡슐화를 향상시킬 때 친구를 사용 하는 것입니다. 가독성과 유용성 (오퍼레이터 << 및 >>이 표준 예제 임)을 향상시키는 것도 좋은 이유입니다.
캡슐화 개선의 예와 같이 다른 클래스의 내부와 함께 작동하도록 특별히 설계된 클래스 (테스트 클래스가 떠오를 것)가 좋은 후보입니다.
<<
이며 >>
일반적으로 멤버 대신 친구입니다. 멤버를 만들면 사용하기 어색해지기 때문입니다. 물론, 그 운영자가 개인 데이터에 액세스해야하는 경우에 대해 이야기하고 있습니다. 그렇지 않으면 우정은 쓸모가 없습니다.
operator<<
및 operator>>
값 클래스의 멤버가 아닌 비회원 또는 회원이 i|ostream
원하는 구문을 제공 할 것이며, 나는 하지 를 제안. " 나는 그 운영자가 개인 데이터에 액세스해야하는 경우에 대해 이야기하고 있습니다. "나는 왜 입력 / 출력 운영자가 개인 멤버에 액세스해야하는지 잘 모르겠습니다.
C ++의 제작자는 캡슐화 원칙을 어 기지 않는다고 말하며 인용 할 것입니다.
"친구"가 캡슐화를 위반합니까? 아니 그렇지 않아. "친구"는 회원 자격과 마찬가지로 액세스 권한을 부여하기위한 명시 적 메커니즘입니다. 표준 준수 프로그램에서는 소스를 수정하지 않고 클래스에 대한 액세스 권한을 부여 할 수 없습니다.
분명하다 ...
TDD를 여러 번 수행하기 위해 C ++에서 'friend'키워드를 사용했습니다.
친구가 내 모든 것을 알 수 있습니까?
업데이트 : Bjarne Stroustrup site 에서 "friend"키워드에 대한 귀중한 답변을 찾았습니다 .
"친구"는 회원 자격과 마찬가지로 액세스 권한을 부여하기위한 명시 적 메커니즘입니다.
friend
키워드 를 사용하는시기와 장소에 대해 매우 신중해야 하며, 귀하와 마찬가지로 나는 거의 사용하지 않았습니다. 다음은 사용 friend
및 대안 에 대한 참고 사항입니다 .
두 객체가 동일한 지 확인하기 위해 두 객체를 비교한다고 가정 해 봅시다. 다음 중 하나를 수행 할 수 있습니다.
첫 번째 옵션의 문제점은 많은 접근자가 직접 변수 액세스보다 약간 느리고 읽기가 어렵고 번거로울 수 있다는 것입니다. 두 번째 접근 방식의 문제점은 캡슐화를 완전히 중단한다는 것입니다.
좋은 점은 클래스의 private 멤버에 여전히 액세스 할 수있는 외부 함수를 정의 할 수있는 것입니다. friend
키워드로 이를 수행 할 수 있습니다 .
class Beer {
public:
friend bool equal(Beer a, Beer b);
private:
// ...
};
이 방법은 equal(Beer, Beer)
현재에 직접 액세스 할 수 있습니다 a
와 b
의 개인 회원 (일 수있는 char *brand
, float percentAlcohol
등이 다소 인위적인 예를 들어, 당신은 빨리 적용 할 것입니다 friend
가 과부하에 == operator
있지만, 우리가 얻을 수 있습니다.
몇 가지 참고할 사항 :
friend
는 클래스의 멤버 함수가 아닙니다public
).나는 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
자주 사용하지 않지만, 지금은 그때마다 필요한 것입니다. 도움이 되었기를 바랍니다!
operator << 및 operator >>와 관련하여 이러한 연산자를 친구로 만들만한 충분한 이유가 없습니다. 그들이 회원 기능이되어서는 안되지만, 친구 일 필요는 없습니다.
가장 좋은 방법은 공개 인쇄 (ostream &) 및 읽기 (istream &) 기능을 만드는 것입니다. 그런 다음 해당 기능의 관점에서 operator << 및 operator >>를 작성하십시오. 이를 통해 해당 기능을 가상으로 만들 수있어 가상 직렬화가 가능하다는 이점이 있습니다.
보호 된 기능을 단위 테스트하기 위해 친구 키워드 만 사용하고 있습니다. 보호 기능을 테스트해서는 안된다고 말하는 사람들도 있습니다. 그러나 새로운 기능을 추가 할 때이 유용한 도구를 찾으십시오.
그러나 클래스 선언에서 키워드를 직접 사용하지 않고 멋진 템플릿 해킹을 사용하여이를 달성합니다.
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에서 작동합니다.
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);
이것이 친구 키워드를 이해하는 데 도움이 될 것이라고 생각합니다.
내가 사용하는 특정 인스턴스 friend
는 Singleton 클래스를 만들 때 입니다. 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;
}
friend
않습니다 하지 특정 "정당성"을 필요로 멤버 함수를 추가 할 때하는 것은하지 않습니다.
친구 기능 및 클래스는 일반적인 경우 캡슐화가 깨지지 않도록 개인 및 보호 된 클래스 멤버에게 직접 액세스 할 수 있습니다. 대부분의 사용법은 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의 친구로 선언 한 것과 같습니다. 컴파일러는 함수 호출을 최적화하여 결과가 동일합니다. 직접 액세스 지침.
'친구'를 사용하는 것은 논쟁의 여지가 있지만 확실한 비용이 드는 지름길이라고 생각합니다.
이것은 실제 사용 사례 상황이 아닐 수도 있지만 클래스 간 친구 사용을 설명하는 데 도움이 될 수 있습니다.
클럽 하우스
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는 다른 멤버 클래스가 제한되어있는 양방향 액세스 거리입니다.
다른 클래스 (서로 상속되지 않음)가 다른 클래스의 개인 또는 보호 멤버를 사용하는 경우 우정을 사용할 수 있습니다.
친구 기능의 일반적인 사용 사례는 개인 또는 보호 된 구성원 모두에 액세스하는 서로 다른 두 클래스 사이에서 수행되는 작업입니다.
에서 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;
}
아마도 위의 답변에서 무언가를 놓쳤지만 캡슐화의 또 다른 중요한 개념은 구현을 숨기는 것입니다. 개인 데이터 멤버 (클래스의 구현 세부 사항)에 대한 액세스를 줄이면 나중에 코드를 훨씬 쉽게 수정할 수 있습니다. 친구가 개인 데이터에 직접 액세스하는 경우 구현 데이터 필드 (개인 데이터)를 변경하면 해당 데이터에 액세스하는 코드가 손상됩니다. 액세스 방법을 사용하면 대부분이 문제가 해결됩니다. 상당히 중요하다고 생각합니다.
당신은 할 수 엄격한과 순수한 OOP 원칙을 준수하고 모든 클래스에 대한 데이터 멤버도이 없도록 접근을 모든 개체 수 있도록 해야한다 간접 통해 그들에 행동 할 수있는 유일한 방법으로 자신의 데이터에 대해 알 수있는 유일한 사람이 될 메시지 , 즉, 방법.
그러나 C #에도 내부 가시성 키워드가 있으며 Java에는 기본적으로 패키지 수준의 액세스 가능성이 있습니다. C ++은 실제로 다른 클래스와 다른 클래스 만 볼 수 있는 클래스를 정확하게 지정하여 클래스에 대한 가시성의 타협을 minimizinbg가 이상적인 OOP에 더 가깝 습니다.
나는 실제로 C ++을 사용하지 않지만 C #에 friend 가 있으면 어셈블리 전역 내부 수정 자 대신 실제로 많이 사용합니다. .NET에서 배포 장치가 있기 때문에 정말, incapsulation을 아프게하지 않는 것입니다 어셈블리.
그러나 Cross-Assembly Friend 메커니즘 처럼 작동 하는 InternalsVisibleTo Attribute (otherAssembly)가 있습니다. Microsoft는이를 시각적 디자이너 어셈블리에 사용합니다.
친구는 콜백에도 유용합니다. 콜백을 정적 메소드로 구현할 수 있습니다
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;
}
다운 스트림에서 볼 수없는 것을 숨기는 것이 더 쉬워집니다.