3つのC++クラスFooA、FooB、およびFooCがあるとします。
FooAにはHello
という名前のメンバー関数があり、この関数をクラスFooBで呼び出したいのですが、クラスFooCで呼び出せないようにします。これを実現するために理解できる最善の方法は、FooBをFooAのフレンドクラスとして宣言することです。しかし、これを行う限り、all FooAのプライベートおよび保護されたメンバーが公開されますが、これは私にはまったく受け入れられません。
したがって、このジレンマを解決できるfriend
クラスよりも優れたC++(03または11)のメカニズムがあるかどうかを知りたいです。
そして、次の構文が可能であれば、それは素晴らしいと思います:
class FooA
{
private friend class FooB:
void Hello();
void Hello2();
private:
void Hello3();
int m_iData;
};
class FooB
{
void fun()
{
FooA objA;
objA.Hello() // right
objA.Hello2() // right
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error
}
};
クラスを特定の関数のフレンドにすることはありませんが、FooB
をプライベートコンストラクタで「キー」クラスのフレンドにしてから、FooA::Hello
そのクラスを無視されたパラメーターとして使用します。 FooC
はパラメーターを提供できないため、Hello
を呼び出すことはできません。
ここで Attorney-Client を使用できると思います。
あなたの場合、例は次のようになります
class FooA
{
private:
void Hello();
void Hello2();
void Hello3();
int m_iData;
friend class Client;
};
class Client
{
private:
static void Hello(FooA& obj)
{
obj.Hello();
}
static void Hello2(FooA& obj)
{
obj.Hello2();
}
friend class FooB;
};
class FooB
{
void fun()
{
FooA objA;
Client::Hello(objA); // right
Client::Hello2(objA); // right
//objA.Hello3() // compile error
//ojbA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
/*FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error*/
}
};
いいえ、これは実際には制限ではありません。私の考えでは、制限はfriend
-デザインの欠陥をハックするための鈍器-が最初に存在するということです。
あなたのクラスFooA
にはFooB
とFooC
を知っているビジネスはありませんおよび「どちらを使用できるか」。パブリックインターフェイスが必要です。not care誰が使用できるか。それがインターフェースのポイントです!そのインターフェース内の関数の呼び出しは、alwaysFooA
をニース、安全、ハッピー、一貫した状態にしておく必要があります。
そして、もしあなたが意図していなかった場所からFooA
インターフェースを誤って使用するかもしれないという懸念があるなら、それをしないでください。 C++は、これらの種類のユーザーエラーに対する保護に適した言語ではありません。この場合、テストカバレッジで十分です。
厳密に言えば、恐ろしく複雑な「デザインパターン」を使用して、必要な機能を取得できると確信していますが、正直なところ、気にしません。
これがプログラムの設計のセマンティクスに問題がある場合、設計に欠陥があることを丁寧にお勧めします。
最も安全な解決策は、2つのクラスの1つをfriend.
にするのではなく、2つのクラスの「仲介者」として別のクラスを使用することです。これを行う1つの方法は、@ ForEveRプロキシクラスやその他の適用可能なデザインパターンについて検索します。
インターフェイスクラスから継承することで、指定したクライアントにクラスのインターフェイスを部分的に公開できます。
class FooA_for_FooB
{
public:
virtual void Hello() = 0;
virtual void Hello2() = 0;
};
class FooA : public FooA_for_FooB
{
private: /* make them private */
void Hello() override;
void Hello2() override;
private:
void Hello3();
int m_iData;
};
class FooB
{
void fun()
{
FooA objA;
FooA_for_FooB &r = objA;
r.Hello() // right
r.Hello2() // right
objA.Hello3() // compile error
objA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
objA.m_iData = 0; // compile error
}
};
ここで、アクセス制御は基本クラスFooA_for_FooB
によって強化されています。タイプFooA_for_FooB
の参照により、FooB
はFooA_for_FooB
内で定義されたメンバーにアクセスできます。ただし、FooC
のプライベートメンバーとしてオーバーライドされているため、FooA
はそれらのメンバーにアクセスできません。 FooC
内でFooA_for_FooB
型、またはFooB
を除くその他の場所を使用しないことで目的を達成できます。
このアプローチはfriend
を必要とせず、物事を単純にします。
同様のことは、すべてを基本クラスでプライベートにし、派生クラスで一部のメンバーをパブリックとして選択的にラップして公開することで実行できます。ただし、この方法ではsometimesいダウンキャストが必要になる場合があります。 (基本クラスはプログラム全体の「通貨」になるためです。)
継承が必要です。これを試して:
// _ClassA.h
class _ClassA
{
friend class ClassA;
private:
//all your private methods here, accessible only from ClassA and _ClassA.
}
// ClassA.h
class ClassA: _ClassA
{
friend class ClassB;
private:
//all_your_methods
}
このようにすると:ClassB
がClassA
を使用できるようになります。 ClassB
はアクセスできません_ClassA
メソッド、プライベートです。
friend
の全体的なアイデアは、クラスを友人に公開することです。
公開する内容をより具体的に指定できる方法は2つあります。
FooA
から継承します。これにより、プロテクトメソッドとパブリックメソッドのみが公開されます。
特定のメソッドとのみ友達になり、そのメソッドのみがアクセスできます:
。
friend void FooB::fun();