97

C++で友情が少なくともオプションで継承できないのはなぜですか? 推移性と再帰性が明らかな理由で禁止されていることは理解していますが (単純な FAQ の引用の回答を避けるためにこれを言っているだけです)、virtual friend class Foo;パズルの線に沿った何かが欠けていることに私は戸惑っています。この決定の背後にある歴史的背景を知っている人はいますか? 友情は本当に単なる限定的なハックであり、その後、いくつかのあいまいな立派な用途に発展しましたか?

明確にするために編集: A の子が B のいずれかに、または B とその子の両方にさらされる場所ではなく、次のシナリオについて話しています。オプションで、フレンド関数のオーバーライドなどへのアクセスを許可することも想像できます。

class A {
  int x;
  friend class B;
};

class B {
  // OK as per friend declaration above.
  void foo(A& a, int n) { a.x = n; }
};

class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };

受け入れられた答え: Loki が述べているように、フレンドシップされた基本クラスで保護されたプロキシ関数を作成することにより、効果は多かれ少なかれシミュレートできるため、クラスまたは仮想メソッド階層にフレンドシップを付与する厳密な必要性はありません。ボイラープレート プロキシ (フレンドシップ ベースが事実上そうなります) の必要性は嫌いですが、これは、ほとんどの場合誤用される可能性が高い言語メカニズムよりも好ましいと見なされたと思います。これらのタイプの質問に対するより良い洞察を得るために、おそらくStroupstrupのThe Design and Evolution of C++を購入して読む時が来たと思います.

4

10 に答える 10

94

と書くことがありますのでFoo、その友人Bar(信頼関係があります)と書いてあります。

しかし、 から派生したクラスを書いている人を信頼できBarますか?
あまり。したがって、彼らは友情を受け継ぐべきではありません。

クラスの内部表現を変更すると、その表現に依存するすべてのものを変更する必要があります。したがって、クラスのすべてのメンバーと、クラスのすべての友達を変更する必要があります。

したがって、 の内部表現Fooが変更された場合は、Barも変更する必要があります (友情は に密接に結びついBarているためFoo)。友情が継承された場合、 から派生したすべてのクラスBarも に緊密にバインドされるため、の内部表現が変更されたFoo場合は変更が必要になります。Fooしかし、私は派生型についての知識がありません(また、派生型についても知りません。別の会社などによって開発されている可能性さえあります)。したがって、Foo変更するとコード ベースに重大な変更が発生するため、変更できません ( から派生したすべてのクラスを変更することはできませんでしたBar)。

したがって、友情が継承された場合、クラスを変更する機能に誤って制限を導入していることになります。これは、基本的にパブリック API の概念を役に立たなくするため、望ましくありません。

注: の子はを使用してBarアクセスできます。メソッドを保護するだけです。次に、 の子は、親クラスを介して呼び出して にアクセスできます。FooBarBarBarFoo

これは、あなたの望むことですか?

class A
{
    int x;
    friend class B;
};

class B
{
    protected:
       // Now children of B can access foo
       void foo(A& a, int n) { a.x = n; }
};

class D : public B
{
    public:
        foo(A& a, int n)
        {
            B::foo(a, n + 5);
        }
};
于 2010-08-25T00:45:50.043 に答える
54

C++で友情が少なくともオプションで継承できないのはなぜですか?

あなたの最初の質問への答えは、「あなたのお父さんの友達はあなたのプライベートにアクセスできますか?」という質問にあると思います。

于 2014-06-19T10:33:50.407 に答える
11

フレンド クラスは、アクセサー関数を介してフレンドを公開し、それらを介してアクセスを許可する場合があります。

class stingy {
    int pennies;
    friend class hot_girl;
};

class hot_girl {
public:
    stingy *bf;

    int &get_cash( stingy &x = *bf ) { return x.pennies; }
};

class moocher {
public: // moocher can access stingy's pennies despite not being a friend
    int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

これにより、オプションの推移性よりも細かい制御が可能になります。たとえば、実行時に制限されたアクセスのプロトコルである、または実施する可能get_cash性があります。protected

于 2010-08-25T04:46:39.943 に答える
9

C++ 標準、セクション 11.4/8

友情は継承されず、推移的でもありません。

友情が継承されると、友人になることを意図していないクラスが突然クラスの内部にアクセスできるようになり、カプセル化に違反します。

于 2010-08-25T00:59:02.207 に答える
2

それはただ不必要だからです。

キーワードの使用friend自体は疑わしいです。結合に関しては、それは最悪の関係です(継承と構成よりもはるかに進んでいます)。

クラスの内部に変更を加えると、このクラスの友達に影響を与えるリスクがあります...本当に不明な数の友達が必要ですか?それらを継承する人も友達である可能性がある場合は、それらをリストすることさえできず、毎回クライアントのコードを壊すリスクがあります。これは確かに望ましくありません。

私は、宿題/ペットプロジェクトの場合、依存関係がしばしば遠い考慮事項であることを自由に認めます。小さなサイズのプロジェクトでは、それは問題ではありません。しかし、複数の人が同じプロジェクトに取り組み、これが数万行に増えるとすぐに、変更の影響を制限する必要があります。

これは非常に単純なルールをもたらします:

クラスの内部を変更すると、クラス自体にのみ影響するはずです

もちろん、あなたはおそらくその友達に影響を与えるでしょうが、ここには2つのケースがあります:

  • フレンドフリー関数:とにかくメンバー関数の方が多いと思います(私はstd::ostream& operator<<(...)ここで考えていますが、これは純粋に言語規則の偶然によるメンバーではありません
  • 友達クラス?実際のクラスに友達クラスは必要ありません。

簡単な方法の使用をお勧めします。

class Example;

class ExampleKey { friend class Example; ExampleKey(); };

class Restricted
{
public:
  void forExampleOnly(int,int,ExampleKey const&);
};

この単純なKeyパターンにより、実際に内部へのアクセスを許可せずに(ある意味で)友人を宣言できるため、変更から友人を隔離できます。さらに、必要に応じて、この友人がその鍵を(子供などの)受託者に貸すことができます。

于 2010-08-25T06:36:49.437 に答える
0

派生クラスは、ベースの「メンバー」である何かのみを継承できます。フレンド宣言は、フレンドリングクラスのメンバーではありません。

$ 11.4 / 1-「...友人の名前はクラスの範囲内にありません。また、別のクラスのメンバーでない限り、友人はメンバーアクセス演算子(5.2.5)で呼び出されません。」

$ 11.4-「また、friendクラスのbase-clauseはメンバー宣言の一部ではないため、friendクラスのbase-clauseは、フレンドシップを付与するクラスからプライベートメンバーと保護されたメンバーの名前にアクセスできません。」

そしてさらに

$ 10.3 / 7-"[注:仮想指定子はメンバーシップを意味するため、仮想関数を非メンバー(7.1.2)関数にすることはできません。仮想関数呼び出しは特定のオブジェクトに依存しているため、仮想関数を静的メンバーにすることもできません。呼び出す関数を決定します。あるクラスで宣言された仮想関数は、別のクラスでフレンドとして宣言できます。] "

'friend'はそもそも基本クラスのメンバーではないので、派生クラスにどのように継承できますか?

于 2010-08-25T01:59:27.743 に答える
0

推測: クラスが他のクラス/関数をフレンドとして宣言する場合、それは、2 番目のエンティティが最初のエンティティへの特権アクセスを必要とするためです。最初のエンティティから派生した任意の数のクラスへの特権アクセスを 2 番目のエンティティに許可することに、どのような用途がありますか?

于 2010-08-24T22:58:13.757 に答える
0

クラス内のフレンド関数は、extern プロパティを関数に割り当てます。つまり、extern は、関数がクラス外のどこかで宣言および定義されていることを意味します。

したがって、フレンド関数はクラスのメンバーではないことを意味します。したがって、継承では、外部のものではなく、クラスのプロパティのみを継承できます。また、フレンド関数で継承が許可されている場合は、サードパーティ クラスが継承されます。

于 2014-11-26T12:23:00.397 に答える
0

単純な論理: 「私には友人のジェーンがいます。私たちが昨日友達になったからといって、彼女のすべての友達が私のものになるわけではありません.

私はまだそれらの個々の友情を承認する必要があり、信頼のレベルはそれに応じて異なります.

于 2019-02-16T17:21:32.050 に答える