C ++フレンドクラスを使用するユースケースは何ですか?


8

C ++の友達を理解しようとしています。友だちを使う良いユースケースはいつですか?別のクラスが別のクラスの属性にアクセスできるようにしたいのであれば、それを単にパブリックとして作成するか、代わりにそのクラスから継承しないのですか?

ご協力ありがとうございました。


4
私はこの質問が間違いなくここにあると思う 「どうやって?」よりも質問 質問、そして答えにはすでにいくつかの良い点がありました。
Larry Coleman

回答:


14

クラスのメンバーを作成することは、すべてのユーザーにクラスへのアクセスをpublic許可し、カプセル化を完全に解除することを意味します。

フレンドクラスがサブクラスではない場合、クラスからの継承は望ましくありません。クラスの内部にアクセスするためだけにサブクラス化することは、重大な設計ミスです。そして、サブクラスでさえ、そのベースクラスのプライベートメンバーを見ることができません。

の典型的な使用法はfriend、ストリーム演算子などのメンバーにできない演算子の場合ですoperator+。これらの場合、それらが関連付けられている実際のクラスは(常に)関数の最初のパラメーターではないため、関数はメンバーメソッドとして実装されます。

別の例は、コレクションのイテレータの実装です。イテレータ(イテレータのみ)は、親コレクションの内部を見る必要がありますが、コレクションのサブクラスではありません。


時には、これらの無料のフレンド関数がクラス内でインラインで定義されます。

3

C ++で使用されるフレンドクラスを最もよく見た例は、単体テストです。単体テストは通常​​、内部についてすべてを知りたいが、あなたの一部ではないので、継承を試みても意味がありません。

単体テストの記述に慣れていない場合は、すぐに始めることをお勧めします。


6
本番クラスは、単体テストクラスについて何も知らないはずです。そして、テストはテストされたクラスの内部を見る必要はありません。適切に設計されたクラスは、そのパブリックAPIを介してユニットテストできます。そうでない場合は、適切に設計されたクラスではない可能性があります(たとえば、あまりに多くのことを試みているため、機能の一部を別のクラスに抽出する必要があります)。
ペーテルTörök

4
@ peter-torok:それは、「うまく設計されている」があなたにとって何を意味するかによります。特権アクセスが必要になる一般的なケースは、モックを挿入して、発生するはずだったアクションが実行されたかどうかを追跡することです。オブジェクト構造のこれらの部分をパブリックAPIで公開することは、カプセル化の主要な違反のようです。したがって、私はしばしばユニットテストコードに特権アクセスを許可します。
btilly '17

1
@btilly:モックを挿入する場合は、コンストラクターでパラメーターを使用できます。クラスの内部にアクセスする必要はありません。
Giorgio

1
@PéterTörökGTestはあなたに同意しません。フレンドテストが役立つ場合があります。たとえば、APIで等価操作を公開せずに(おそらくセキュリティ上の理由から)同じクラスの2つのオブジェクトの等価を比較したい場合があります。
VF1 2015年

1
@Giorgioはい、単体テストしたい情報を公開することができます。しかし、今やあなたのパブリックAPIは成長し、将来は賢明でないであろう決定を設計するためにあなたをロックするかもしれません。言い換えれば、現在の実装のエッジケースの単体テストを行うことが適切です。そのレベルの詳細をクラスのコンシューマーに公開することが常に適切であるとは限りません。
btilly 2015年

0

この質問は、stackoverflowに関するものです。とにかく、クラスのプライベート部分を別のクラス(複数の場合もある)と共有したいが他の誰とも共有したくない場合にのみ、友達を使用します。あなたがそれらを公開すると、誰もがあなたの私的な部分を見ることができます プライバシーを強制する重要な制限が2つあります。1)友達を特定する必要があります。他の誰も友達にはなれません。2)フレンドクラスのサブクラスで「フレンドリ」な動作を継承できない


1
ソクラテス方式の質問:そして、なぜ皆にあなたの私的な部分を見せるのが悪いのですか?
Larry Coleman

3
@ラリー:上品に行われたかどうか、またはショックの価値によって異なります:D
マットエレン

@マット:パブリック変数の上品な使い方はありますか?
Larry Coleman

@ラリー:(非予言的ではなく)オブジェクトの制御外でオブジェクトの状態を変更しても、オブジェクトが無効な状態になることはありません。この点でc#のイベントは重要だと思います。
マットエレン

0

典型的な使用法は、クラスのインターフェースの一部を形成する関数へのアクセスを許可することですが、他のルールのおかげで、実際には適切なクラスの一部になることはできません。iostreamの挿入子/抽出子は、典型的な例です。

namespace whatever { 
    class something { 
        // ...    
        friend std::ostream &operator<<(std::ostream &os, something const &thing);
        friend std::istream &operator>>(std::istream &is, something &thing);
    };
}

これらのオペレーターをクラスのメンバーにして機能しません。I / O操作は次のようになります。

whatever::something thing;

std::cout << thing;

メンバー関数として実装された演算子の場合、これは次のように解決されます。

std::cout.operator<<(thing);

つまり、関数はのメンバーでstd::coutなく、のメンバーである必要ありsomethingます。std::ostream常に変更したくないので、メンバー関数ではなくフリー関数で演算子をオーバーロードするのが唯一の合理的なオプションです。これにより、カプセル化を完全に無視してクラスのすべてをパブリックにするか、プライベートにして、本当に必要ないくつかのアクセスを許可するかの2つの可能性が残されます。

別のクラスを友達にすることはあまり一般的ではありません。この場合、通常はモジュールまたはサブシステムの順序で何かを作成します。これは、連携して動作し、世界全体に許可されていない、互いにある程度の特別なアクセス権を持つクラスのセットです。


0

C#のフレンドライクなクラスに対するこのリクエストには、それらを持たない言語であるフレンドクラスの必要性のかなり良い例があります。

ここで引用された卸売:

.NETのアクセス修飾子は、コードがどのように機能するかを同僚に信頼してもらい、バグを引き起こさないように設計されています。

しかし、この考えには欠陥があります。ソフトウェア開発は非常に複雑であるため、経験豊富なプログラマーは自分自身も信用しないことを知っています。このため、経験豊富なプログラマーは、設計上、偶発的な誤用で壊れないコードを書くことを好みます。.NETは、1つのクラスのみが関係する場合にこれを行うためのツールを提供しますが、プログラマーが2つまたは3つの相互作用するクラスの防弾セットを作成しようとした瞬間、これは失敗します。

これに対する.NETが意図するアプローチは、共有メンバーを「内部」としてマークすることです。これにより、クラスは相互の内部と対話できます。残念ながら、これにより、アセンブリ内の他のすべてのクラスが、偶然または意図的に、内部構造を混乱させることもできます。

C#Language PMのMads Torgersenはこの提案にさえ返信しており、懸念は確かに有効なものであると感じていますが、そこで提案されている特定の実装については不明であると述べています。

C ++ friendは、説明されている問題に取り組む方法の1つにすぎません。


0

クラスに一夫一婦制の関係を持たせたい場合。

たとえば、不明なクラスにプライベートにアクセスさせたくないが、パートナーにはアクセスが必要な場合。


C#の内部およびJavaのパッケージのユースケースと同様です。
ダニーVarod

0

Javaでパッケージアクセスを使用した場合に、C ++のフレンドクラスが役立つことがわかりました。つまり、同じソースファイルに1つの単位として記述および維持されるほど密接に関連している一連のクラスがあります。それらの1つに取り組んでいる誰もが実際にそれらすべてに取り組んでいます。彼らが維持する不変量は、一緒に維持します。彼らがお互いの内部を見て、共有された不変量を維持できるようにすることは理にかなっています。

意味をなさないのは、保護されたアクセスです。私の内部の一部を公開するのが安全ではない場合、完全に無関係なプロジェクトで後で発生し、私のサブクラスであると主張するどんなジャッカナップにもそれを公開することは安全ではありません。他の人と同じように、サブクラスが私の公開インターフェースを使用できるようにします。(彼らが友達でない限り、しかしその場合私はそれらを書いて同じソースに実装したので、他のプロジェクトからはほとんど出てこない。)


-1

プログラム内の2つの異なるクラスのプライベートまたは保護されたメンバーに対して操作を実行する必要がある外部関数があるとしましょう。ここで、その関数がアクセスしてこれらのクラスメンバーを実際に使用するためには、両方のクラスの友達にする必要があります。これらの両方のクラスの友達にすることで、それをクラスのメンバーにしないことは、これらのクラスのプライベートメンバーまたは保護されたメンバーを参照することになります。2つの異なるクラスのオブジェクトを操作する場合は、フレンド関数を使用する必要があると言えます。ただし、フレンド関数とクラスを使いすぎると、カプセル化の価値が低下する可能性があります。


2
この投稿は読みにくいです(テキストの壁)。より良い形に編集していただけませんか?
2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.