web-dev-qa-db-ja.com

「友人」クラスが一部のプライベートメンバーのみにアクセスできるようにする

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
    }
};
32
Archer

クラスを特定の関数のフレンドにすることはありませんが、FooBをプライベートコンストラクタで「キー」クラスのフレンドにしてから、FooA::Helloそのクラスを無視されたパラメーターとして使用します。 FooCはパラメーターを提供できないため、Helloを呼び出すことはできません。

このキー指向のアクセス保護パターンは既知のイディオムですか?

29
Steve Jessop

ここで 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*/
    }
};
23
ForEveR

いいえ、これは実際には制限ではありません。私の考えでは、制限はfriend-デザインの欠陥をハックするための鈍器-が最初に存在するということです。

あなたのクラスFooAにはFooBFooCを知っているビジネスはありませんおよび「どちらを使用できるか」。パブリックインターフェイスが必要です。not care誰が使用できるか。それがインターフェースのポイントです!そのインターフェース内の関数の呼び出しは、alwaysFooAをニース、安全、ハッピー、一貫した状態にしておく必要があります。

そして、もしあなたが意図していなかった場所からFooAインターフェースを誤って使用するかもしれないという懸念があるなら、それをしないでください。 C++は、これらの種類のユーザーエラーに対する保護に適した言語ではありません。この場合、テストカバレッジで十分です。

厳密に言えば、恐ろしく複雑な「デザインパターン」を使用して、必要な機能を取得できると確信していますが、正直なところ、気にしません。

これがプログラムの設計のセマンティクスに問題がある場合、設計に欠陥があることを丁寧にお勧めします。

最も安全な解決策は、2つのクラスの1つをfriend.にするのではなく、2つのクラスの「仲介者」として別のクラスを使用することです。これを行う1つの方法は、@ ForEveRプロキシクラスやその他の適用可能なデザインパターンについて検索します。

1

インターフェイスクラスから継承することで、指定したクライアントにクラスのインターフェイスを部分的に公開できます。

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の参照により、FooBFooA_for_FooB内で定義されたメンバーにアクセスできます。ただし、FooCのプライベートメンバーとしてオーバーライドされているため、FooAはそれらのメンバーにアクセスできません。 FooC内でFooA_for_FooB型、またはFooBを除くその他の場所を使用しないことで目的を達成できます。

このアプローチはfriendを必要とせず、物事を単純にします。

同様のことは、すべてを基本クラスでプライベートにし、派生クラスで一部のメンバーをパブリックとして選択的にラップして公開することで実行できます。ただし、この方法ではsometimesいダウンキャストが必要になる場合があります。 (基本クラスはプログラム全体の「通貨」になるためです。)

1
rolevax

継承が必要です。これを試して:

// _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
}

このようにすると:ClassBClassAを使用できるようになります。 ClassBはアクセスできません_ClassAメソッド、プライベートです。

0
user3737631

friendの全体的なアイデアは、クラスを友人に公開することです。

公開する内容をより具体的に指定できる方法は2つあります。

  1. FooAから継承します。これにより、プロテクトメソッドとパブリックメソッドのみが公開されます。

  2. 特定のメソッドとのみ友達になり、そのメソッドのみがアクセスできます:

 friend void FooB::fun();
0
Yochai Timmer