C ++で 'friend'を使用する必要があるのはいつですか?


354

私はC ++のFAQを読んでいて、そのfriend宣言に興味がありました。個人的には使用したことがありませんが、言語を探求することに興味があります。

使用の良い例は何friendですか?


FAQをもう少し読むと、 << >>演算子をオーバーロードして、それらのクラスの友達として追加です。しかし、これがカプセル化を壊さないかどうかはわかりません。これらの例外はいつOOPである厳密性の範囲内にとどまることができますか?


5
友人クラスは必ずしも悪いものではないという答えには同意しますが、私はそれを小さなコードとして扱う傾向があります。常にではありませんが、多くの場合、クラス階層を再検討する必要があることを示しています。
Mawgはモニカを復活させる'09 / 09/23

1
すでに密結合している友達クラスを使用します。それはそれのために作られています。たとえば、データベーステーブルとそのインデックスは密接に結合されています。テーブルが変更されると、そのすべてのインデックスを更新する必要があります。したがって、DBIndexクラスはDBTableをフレンドとして宣言し、DBTableがインデックスの内部に直接アクセスできるようにします。しかし、DBIndexへのパブリックインターフェイスはありません。インデックスを読み取っても意味がありません。
shawnhcorey 2017年

実践的な経験がほとんどないOOPの「純粋主義者」は、クラスはそのプライベートな状態の唯一の管理者であるべきであるため、友人はOOPの原則に違反していると主張しています。これは、2つのクラスが共有プライベート状態を維持する必要があるという一般的な状況に遭遇するまでは問題ありません。
kaalus

回答:


335

まず(IMO)言う人の言うことを聞かない friend役に立たない。それは便利です。多くの状況では、一般に公開することを意図していないデータまたは機能を持つオブジェクトがあります。これは特に、さまざまな領域に表面的にしか慣れていない多くの作成者がいる大規模なコードベースに当てはまります。

フレンド指定子に代わるものはありますが、それらはしばしば扱いにくい(cppレベルの具象クラス/マスクされたtypedef)か、絶対確実ではありません(コメントまたは関数名の規則)。

答えに。

friend指定子は友人文を作り、クラス内の保護されたデータや機能に指定されたクラスのアクセスを可能にします。たとえば、次のコードでは、誰でも子供に名前を尋ねることができますが、名前を変更できるのは母親と子供だけです。

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 );
};

114
補足として、C ++ FAQはカプセル化をfriend 強化することについて言及しています。と同様に、メンバーに選択的なアクセスfriend許可します。きめ細かな制御は、パブリックアクセスを許可するよりも優れています。他の言語も選択的アクセスメカニズムを定義しています。C#を検討してください。の使用に関するほとんどの否定的な批判は、より悪いカップリングに関連しています。ただし、場合によっては、より密な結合がまさに必要なものであり、その力を発揮します。protectedinternalfriendfriend
アンドレ・キャノン

5
Andrewの(cppレベルの具象クラス)と(マスクされたtypedef)について詳しく教えてください。
OmarOthman

18
この回答はfriendやる気を起こさせる例を提供するよりも、何を説明することに重点が置かれているようです。Window / WindowManagerの例は、示されている例よりも優れていますが、あいまいです。この回答では、質問のカプセル化の部分についても触れていません。
bames53

4
C ++には、すべてのメンバーが実装の詳細を共有できるパッケージの概念がないため、実際には「友達」が存在しますか?私は実際の例に本当に興味があります。
weberc2 2014年

1
母子の例は残念です。これらの名前はインスタンスには適していますが、クラスには適していません。(問題は、2人の母親がいて、それぞれに自分の子供がいる場合に発生します)。
Jo So

162

職場では、コードのテストに友人を広範囲に使用しています。これは、メインアプリケーションコードに適切なカプセル化と情報非表示を提供できることを意味します。しかし、友人を使用して内部状態とテスト用のデータを検査する個別のテストコードを使用することもできます。

デザインの重要なコンポーネントとして、フレンドキーワードを使用しないと言うだけで十分です。


それはまさに私がそれを使用するものです。または、メンバー変数を保護に設定するだけです。C ++ / CLIでは機能しないのは残念です:-(
Jon Cage

12
個人的に私はこれを思いとどまらせます。通常、インターフェースをテストします。つまり、入力のセットが期待される出力のセットを提供します。内部データを検査する必要があるのはなぜですか?
Graeme

55
@Graeme:優れたテスト計画には、ホワイトボックスとブラックボックスの両方のテストが含まれているためです。
Ben Voigt 2013

1
この回答で完全に説明されているように、@ Graemeに同意する傾向があります
Alexis Leclerc

2
@Graeme直接内部データではないかもしれません。他のオブジェクトがそのクラスの保護されたメソッドに独自のデータをフィードまたはシードする必要がある場合、そのメソッドがクラスに対してプライベートであり、パブリックにアクセスできないようにして、そのデータに対して特定の操作またはタスクを実行するメソッドである可能性があります。
Francis Cugler、2017

93

friendキーワードは良い多くの用途があります。これは私にすぐに見える2つの使用法です。

友達の定義

フレンド定義ではクラススコープで関数を定義できますが、関数はメンバー関数としては定義されませんが、それを囲む名前空間のフリー関数として定義され、引数に依存するルックアップを除いて通常は表示されません。これにより、演算子のオーバーロードに特に役立ちます。

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
}

プライベートCRTP基本クラス

場合によっては、ポリシーが派生クラスにアクセスする必要があることがわかります。

// 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ベースはこのポインターをキャストして、データメンバーポインターを使用して派生クラスのデータフィールドにアクセスできるようにします。


こんにちは、CRTPを試してみると、(xcode 4で)構文エラーが発生します。Xcodeは、クラステンプレートを継承しようとしていると考えています。エラーが発生した時P<C>template<template<typename> class P> class C : P<C> {};「クラステンプレートCの使用はテンプレート引数が必要」述べました。あなたは同じ問題を抱えていたり、おそらく解決策を知っていますか?
bennedich

@bennedichは一見すると、不十分なC ++機能のサポートで発生するようなエラーのように見えます。これはコンパイラの間でかなり一般的です。FlexibleClass内での使用は、FlexibleClass暗黙的にそれ自身のタイプを参照する必要があります。
Yakk-Adam Nevraumont 2012

@bennedich:クラス本体内からクラステンプレートの名前を使用するためのルールがC ++ 11で変更されました。コンパイラでC ++ 11モードを有効にしてみてください。
Ben Voigt 2013

Visual Studio 2015で、次のパブリックを追加します:f(){}; f(int_type t):value(t){}; このコンパイラエラーを回避するには:エラーC2440: '<function-style-cast>': 'utils :: f :: int_type'から 'utils :: f'に変換できません注:コンストラクターはソースタイプまたはコンストラクターを取得できません過負荷の解決があいまいでした
Damian、

41

@roo:クラス自体がプライベートメンバーにアクセスできるユーザーを指示するため、カプセル化はここでは壊れていません。カプセル化は、これがクラスの外から引き起こされる可能性がある場合にのみ壊れます。たとえば、operator <<「私はクラスの友達です」と宣言した場合などですfoo

friendの使用でpublicはなくの使用を置き換えますprivate

実際、C ++ FAQはすでにこれに答えています。


14
、私は2番目という「!友人の置き換えは、プライベートの使用、公共のない使用」
ワリードEissa

26
@Assaf:はい、しかしFQAは、ほとんどの場合、実際の価値のない、多くの支離滅裂な怒りの意味不明です。上の部分friendも例外ではありません。ここでの唯一の実際の観察は、C ++がコンパイル時にのみカプセル化を保証することです。そして、あなたはそれを言うのにこれ以上言葉は必要ありません。残りはbollocksです。つまり、要約すると、FQAのこのセクションは言及する価値がありません。
Konrad Rudolph、

12
そのFQAのほとんどは完全なblxです:)
rama-jka toti

1
@Konrad:「ここでの唯一の実際の観察は、C ++がコンパイル時にのみカプセル化を保証することです。」実行時にこれを保証する言語はありますか?私の知る限り、プライベートメンバー(および関数へのポインターまたはファーストクラスオブジェクトとしての関数を許可する言語の場合は関数)への参照を返すことは、C#、Java、Pythonなどで許可されています。
アンドレ・キャノン

@André:JVMとCLRは、私の知る限り、実際にこれを保証できます。それが常に行われるかどうかはわかりませんが、パッケージ/アセンブリをそのような侵入から保護できます(ただし、自分でこれを行ったことはありません)。
Konrad Rudolph

27

正規の例は、operator <<をオーバーロードすることです。もう1つの一般的な用途は、ヘルパーまたは管理者クラスが内部にアクセスできるようにすることです。

C ++の友達について聞いたいくつかのガイドラインを次に示します。最後のものは特に印象的です。

  • あなたの友達はあなたの子供の友達ではありません。
  • あなたの子供の友達はあなたの友達ではありません。
  • 友達だけがあなたのプライベートパーツに触れることができます。

" 正規の例は、operator <<をオーバーロードするfriendことです。
curiousguy

16

編集: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ますか?不明な機能へのアクセスを許可しますか?

いいえ。これにより、1つの関数でクラスのプライベートメンバーにアクセスできます。ちょうどしたようにc2。と同様c2に、アクセス権を持つ1つの関数は、「ランダムで不明な関数」ではなく、「クラス定義にリストされている関数」です。と同様にc2、クラス定義を見るだけで、誰がアクセスできるかの完全なリストを確認できます。

それでは、これはカプセル化されていないのでしょうか?同じ量のコードがクラスのプライベートメンバーにアクセスできます。そして、アクセス権を持つすべての人がクラス定義にリストされます。

friendカプセル化を壊しません。「OOP」と言うと、実際に「Java」を意味するため、一部のJavaの人々はプログラマーを不快に感じます。「カプセル化」と言っても、「プライベートメンバーを任意のアクセスから保護する必要がある」という意味ではなく、「プライベートメンバーにアクセスできる唯一の関数がクラスメンバーであるJavaクラス」です。いくつかの理由

まず、すでに示したように、制限が厳しすぎます。friendメソッドに同じことを許可してはならない理由はありません。

第二に、それは十分に制限的ではありません。4番目のクラスを考えます。

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

これは、前述のJavaの考え方によると、完全にカプセル化されています。 それでも、誰でもxを読んだり修正したりすることができます。それはどういう意味ですか?(ヒント:ありません)

結論:カプセル化とは、プライベートメンバーにアクセスできる関数を制御できることです。これらの関数の定義がどこにあるのかは正確ではありません


10

Andrewの例のもう1つの一般的なバージョン、恐ろしいコードカップレット

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);
}

言い換えると、パブリックインターフェイスを小さく保ち、フレンド関数のクラスとオブジェクトを横断する不変条件を適用できます。


6
なぜ誰かがそのための友達を必要とするのでしょうか?addChildメンバー関数にも親を設定させてみませんか?
Nawaz、2012

1
関数の/ カテゴリでsetParent管理することになるので、クライアントが親を変更できるようにしたくないので、より良い例は友達を作ることです。addChildremoveChild
Ylisar 2014

8

メンバー/機能のアクセス権は、プライベート/保護/パブリック権を使用して制御しますか?したがって、これら3つのレベルのそれぞれの考えが明確であると仮定すると、何かが欠けていることは明らかです...

たとえば、保護されたメンバー/関数の宣言はかなり一般的です。この機能は誰もが手に届かないものです(もちろん、継承された子は除きます)。しかし、例外はどうですか?どのセキュリティシステムでも、何らかの「ホワイトリスト」を使用できますか?

したがって、フレンドは、堅固なオブジェクトの分離を柔軟に持つことができますが、正当化されると感じるものに対して「抜け穴」を作成することを可能にします。

なくても大丈夫なデザインは常にあるので必要ないという人もいると思います。私はそれをグローバル変数の議論に似ていると思います:それらを決して使うべきではありません、それらなしで行う方法は常にあります... ..これは友達と同じケースだと思います。

設定関数を使用せずにメンバー変数にアクセスできることを除いて、それは実際には何の効果もありません

まあそれはそれを正確に見る方法ではありません。アイデアは、WHOが何にアクセスできるかを制御することであり、設定機能の有無は、それとほとんど関係ありません。


2
friend抜け穴はどうですか?これにより、クラスにリストされているメソッドがプライベートメンバーにアクセスできます。それでも、任意のコードがそれらにアクセスすることはできません。そのため、パブリックメンバー関数と同じです。
2009

フレンドは、C ++でC#/ Javaパッケージレベルのアクセスを取得できる限り近くにあります。@jalf-フレンドクラス(ファクトリークラスなど)はどうですか?
Ogre Psalm33 2010

1
@鬼:それらについてはどうですか?あなたはまだそのクラスを明確に与えおり、他の誰もクラスの内部にアクセスできません。クラスを混乱させるために任意の未知のコードのためにゲートを開いたままにするだけではありません。
jalf

8

フレンドアクセスを使用する便利な場所を見つけました:プライベート関数のユニットテスト。


しかし、そのためにパブリック関数も使用できますか?フレンドアクセスを使用する利点は何ですか?
Zheng Qu 2016

@Maverobot質問について詳しく教えてください。
VladimirS

5

コンテナーを作成していて、そのクラスのイテレーターを実装したいときに、Friendは便利です。


4

私が以前働いていた会社で、友人を使ってまともな影響を与えたところ、興味深い問題が発生しました。私はフレームワーク部門で働いて、カスタムOS上に基本的なエンジンレベルのシステムを作成しました。内部的にはクラス構造がありました:

         Game
        /    \
 TwoPlayer  SinglePlayer

これらのクラスはすべてフレームワークの一部であり、私たちのチームによって維持されていました。同社が作成したゲームは、ゲームの子供たちの1人から派生したこのフレームワークの上に構築されました。問題は、ゲームに、SinglePlayerとTwoPlayerがアクセスする必要があるさまざまなものへのインターフェイスがあり、フレームワーククラスの外部に公開したくないということでした。解決策は、これらのインターフェイスをプライベートにし、TwoPlayerおよびSinglePlayerが友情を介してそれらにアクセスできるようにすることでした。

正直なところ、この問題全体は、システムをより適切に実装することで解決できたはずですが、私たちは自分たちが持っていたものに縛られていました。


4

簡単に言えば、実際にカプセル化を改善するときに友人を使用します。読みやすさと使いやすさを向上させることも(演算子<<と>>が標準的な例です)、これも正当な理由です。

カプセル化を改善する例としては、他のクラスの内部で動作するように特別に設計されたクラス(テストクラスが思い浮かびます)が良い候補です。


演算子<<と>>は標準的な例です」「いいえ。むしろ標準的なカウンターの例です。
curiousguy

@curiousguy:演算子を使用する<<>>、通常はメンバーではなく友達になります。メンバーにすると、使いにくくなります。もちろん、私はそれらのオペレーターが個人データにアクセスする必要がある場合について話しています。そうでなければ、友情は役に立たない。
Gorpik

彼らのメンバーが使用してそれらを厄介になるだろう。作るために作る、明らかに」operator<<operator>>の代わりに、非メンバー、またはメンバーの値クラスのメンバーi|ostream、希望の構文を提供しないであろう、と私はない、それを示唆しています。「これらのオペレーターがプライベートデータにアクセスする必要がある場合について話している」入出力オペレーターがプライベートメンバーにアクセスする必要がある理由がよくわかりません。
curiousguy

4

C ++の作成者は、それがカプセル化の原則を壊していないと言っています、そして私は彼を引用します:

「友達」はカプセル化に違反しますか? いいえ、違います。「友達」は、メンバーシップと同様に、アクセスを許可するための明示的なメカニズムです。(標準の適合プログラムでは)ソースを変更せずにクラスへのアクセスを許可することはできません。

はっきりしているだけではありません...


@curiousguy:テンプレートの場合でも、それは本当です。
Nawaz

@Nawazテンプレートの友情が付与される場合がありますが、友情付与クラスを変更しなくても、誰でも新しい部分的または明示的な特殊化を行うことができます。ただし、その際はODR違反に注意してください。とにかくそれをしないでください。
curiousguy

3

別の用途:友人は =>:(「underivableクラスを作る」別名)(+仮想継承)クラスから派生回避するために使用することができます12

2から:

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 

3

TDDを何度も行うために、C ++で 'friend'キーワードを使用しました。

友人は私についてすべてを知ることができますか?


更新:Bjarne Stroustrupのサイトの "friend"キーワードに関するこの貴重な回答を見つけました。

「友達」は、メンバーシップと同様に、アクセスを許可するための明示的なメカニズムです。


3

friendキーワードをいつ、どこで使用するかについては十分に注意する必要があり、あなたのように、私はそれをほとんど使用していません。以下は、いくつかの使用上の注意friendと代替案です。

2つのオブジェクトを比較して、それらが等しいかどうかを確認するとします。次のいずれかを実行できます。

  • アクセサーメソッドを使用して比較を行います(すべてのivarをチェックして同等かどうかを判断します)。
  • または、すべてのメンバーを公開して直接アクセスすることもできます。

最初のオプションの問題は、それが直接変数アクセスよりも(わずかに)遅く、読み取りが難しく、扱いにくい、多くのアクセサになる可能性があることです。2番目のアプローチの問題は、カプセル化を完全に解除することです。

すばらしいのは、クラスのプライベートメンバーにアクセスできる外部関数を定義できることです。これは次のfriendキーワードで実行できます。

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

この方法は、equal(Beer, Beer)今に直接アクセスしているabプライベートメンバーの(かもしれchar *brandfloat percentAlcoholこれはかなり不自然な例であるなど、あなたはすぐに適用されるfriend、オーバーロードに== operator、私たちはそれに取得します。

注意すべきいくつかの点:

  • friendはクラスのメンバー関数ではありません
  • これは、クラスのプライベートメンバーへの特別なアクセス権を持つ通常の関数です
  • すべてのアクセサーとミューテーターを友達に置き換えないでください(すべてを作ることもできますpublic!)
  • 友情は相互的ではない
  • 友情は推移的ではない
  • 友情は受け継がれない
  • または、C ++ FAQで説明されているように、「私に友情のアクセスを許可しても、子供に自動的にアクセスが許可されるわけではなく、友達に自動的にアクセスが許可されるわけでも、自動的にアクセスが許可されるわけでもありません。 」

私は本当にfriends他の方法でそれを行うのが非常に難しい場合にのみ使用します。別の例として、多くのベクトル数学関数はしばしばとして作成されているfriendsの相互運用性のためにMat2x2Mat3x3Mat4x4Vec2Vec3Vec4、などそして、それは友達になるのではなく、どこにでもアクセサを使用する必要がありますするだけでそんなに簡単です。指摘し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頻繁に使用しませんが、時々それはあなたが必要とするものだけです。お役に立てれば!


2

operator <<とoperator >>に関しては、これらの演算子を友達にする理由はありません。彼らがメンバー機能であってはならないのは事実ですが、彼らも友達である必要はありません。

最善の方法は、public print(ostream&)およびread(istream&)関数を作成することです。次に、これらの関数の観点から、operator <<およびoperator >>を記述します。これにより、仮想シリアル化を提供するこれらの関数を仮想化できるという追加の利点が得られます。


operator <<およびoperator >>に関して、これらのオペレーターを友だちにする正当な理由はありません。」絶対に正しい。「これにより、これらの関数を仮想化できるという追加の利点が得られます」問題のクラスが派生を目的としている場合は、そうです。そうでなければ、なぜわざわざ?
curiousguy

なぜこの回答が2度反対票を投じられたのか、そして説明さえもない理由が本当にわかりません!それは失礼です。
curiousguy

virtualは、シリアル化で非常に大きなパフォーマンスヒットを追加します
paulm

2

保護された関数を単体テストするために、フレンドキーワードのみを使用しています。保護された機能をテストすべきではないと言う人もいます。しかし、私は新しい機能を追加するときにこの非常に便利なツールを見つけました。

ただし、クラス宣言で直接キーワードを使用せず、代わりに気の利いたテンプレートハックを使用してこれを実現しています。

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で動作します。


2

C ++では、 "friend"キーワードはオペレーターのオーバーロードとブリッジの作成に役立ちます。

1.)演算子のオーバーロードのFriendキーワード:演算子のオーバーロードの
例は次のとおりです。2つの浮動小数点変数
"x"(x座標用)と "y"(y座標用)を持つクラス "Point"があるとします。次に、"<<"(抽出演算子)をオーバーロードして、"cout << pointobj"とx座標とy座標が出力されるようにする必要があります(pointobjはクラスPointのオブジェクトです)。これを行うには、2つのオプションがあります。

   1.「ostream」クラスの「operator <<()」関数をオーバーロードします。
   2.「Point」クラスの「operator <<()」関数をオーバーロードします。
ここで、最初のオプションは適切ではありません。別のクラスでこの演算子を再度オーバーロードする必要がある場合は、「ostream」クラスを再度変更する必要があるためです。
そのため、2番目が最適なオプションです。これでコンパイラは"operator <<()"関数を呼び出すことができます :

   1. ostreamオブジェクトcout.Asの使用:cout.operator <<(Pointobj)(フォームostreamクラス)。
2.オブジェクトなしで呼び出します。As:<<(cout、Pointobj)(Pointクラスから)。

Pointクラスにオーバーロードを実装したためです。したがって、オブジェクトなしでこの関数を"friend"呼び出すには、オブジェクトなしでフレンド関数を呼び出すことができるため、キーワードを追加する必要があります。:今すぐ関数宣言はしたようになります
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

ブリッジを作る際に2)友達のキーワード:
私たちは2つの以上のクラス(一般的に「ブリッジ」と呼ばれる)のアクセスプライベートメンバに我々が持っている機能を確認する必要がありますと仮定します。これを行う方法:
クラスのプライベートメンバーにアクセスするには、そのクラスのメンバーである必要があります。他のクラスのプライベートメンバーにアクセスするには、すべてのクラスでその関数をフレンド関数として宣言する必要があります。例:2つのクラスAとBがあるとします。関数"funcBridge()"が両方のクラスのプライベートメンバーにアクセスする必要があります。次に、両方のクラスを次のように宣言する必要があり"funcBridge()"ます。
friend return_type funcBridge(A &a_obj, B & b_obj);

これは友達のキーワードを理解するのに役立つと思います。


2

フレンド宣言のリファレンスが言うように:

フレンド宣言はクラス本体に表示され、関数または別のクラスに、フレンド宣言が表示されるクラスのプライベートおよび保護されたメンバーへのアクセスを許可します。

そのfriendため、保護されたメンバーのみにアクセスできるとの回答の一部には、技術的なエラーがあります。


1

ツリーの例はかなり良い例です。継承関係を持たずにオブジェクトをいくつかの異なるクラスに実装することです。

おそらく、コンストラクターを保護して、「友達」ファクトリーを使用するように人々を強制するためにそれが必要になるかもしれません。

...わかりました、まあ、それなしで生きることができます。


1

TDDを何度も行うために、C ++で 'friend'キーワードを使用しました。
友人は私についてすべてを知ることができますか?

いいえ、それは一方的な友情です: `(


1

私が使用する特定のインスタンスの1つfriendは、シングルトンクラスを作成するときです。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;
}

これは好みの問題かもしれませんが、いくつかのキーストロークを保存することがここでの友人の使用を正当化するとは思いません。GetMySingleton()はクラスの静的メソッドである必要があります。
Gorpik、2009年

プライベートc-torは非フレンド関数がMySingletonをインスタンス化することを許可しないため、ここでフレンドキーワードが必要です。
JBRウィルキンソン2009

@Gorpik「これは好みの問題かもしれませんが、いくつかのキーストロークを保存することは、ここでの友人の使用を正当化するとは思いません。」それはそうです。とにかく、メンバー関数を追加する必要friendない場合、特定の「正当化」必要ありません。
curiousguy

シングルトンは悪いとにかく練習「有害シングルトン」(グーグルとみなされ、次のような結果の多くを得るでしょう。この私がその機能を十分に活用考慮することができるアンチパターンを実装する機能を使用しないと思います。。
weberc2

1

Friend関数とクラスは、クラスのプライベートおよび保護されたメンバーへの直接アクセスを提供して、一般的なケースでカプセル化を壊さないようにします。ほとんどの使用法はostreamでの使用です。次のように入力できるようにしたいと思います。

Point p;
cout << p;

ただし、これにはPointのプライベートデータへのアクセスが必要な場合があるため、オーバーロードされた演算子を定義します

friend ostream& operator<<(ostream& output, const Point& p);

ただし、カプセル化には明らかな影響があります。まず、フレンドクラスまたはフレンドクラスは、クラスのすべてのメンバーにフルアクセスできます。第2に、クラスとフレンドの実装は、クラスの内部変更によってフレンドが壊れる可能性のある点にまで統合されています。

友人をクラスの延長として見る場合、これは論理的に言えば問題ではありません。しかし、その場合、そもそもなぜ友達を槍で突く必要があったのでしょうか。

「友達」が達成しようとしているのと同じことを達成するために、カプセル化を壊すことなく、これを行うことができます:

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のフレンドと宣言した場合と同じです。コンパイラーは関数呼び出しを最適化するため、これは同じ結果になります直接アクセスとしての指示。

「友達」を使用することは、議論の余地のあるメリットがあるが、確かなコストのショートカットであると思います。


1

これは実際のユースケースの状況ではないかもしれませんが、クラス間の友達の使用を説明するのに役立つかもしれません。

クラブハウス

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{};

ここでこれらのクラスの関係を見ると、クラブハウスには、さまざまな種類のメンバーシップとメンバーシップアクセスがあります。メンバーはすべて共通または共通のIDと列挙型を共有し、外部クラスが基本クラスにあるアクセス関数を介してそのIDとタイプにアクセスできるため、すべてスーパーまたは基本クラスから派生します。

ただし、Membersとその派生クラスのこの種の階層、およびClubHouseクラスとの関係を通じて、「特別な特権」を持つ派生クラスの1つだけがVIPMemberクラスです。基本クラスと他の2つの派生クラスはClubHouseのjoinVIPEvent()メソッドにアクセスできませんが、VIPメンバークラスは、そのイベントに完全にアクセスできるかのようにその特権を持っています。

VIPMemberとClubHouseを使用すると、他のメンバークラスが制限される双方向のアクセスストリートになります。


0

クラスのツリーアルゴリズムを実装するとき、教授が私たちに提供したフレームワークコードには、ノードクラスのフレンドとしてツリークラスがありました。

設定関数を使用せずにメンバー変数にアクセスできることを除いて、実際には何の効果もありません。


0

異なるクラス(互いに継承していないクラス)が他のクラスのプライベートまたは保護されたメンバーを使用している場合、友情を使用できます。

フレンド関数の典型的な使用例は、両方のプライベートまたは保護されたメンバーにアクセスする2つの異なるクラス間で実行される操作です。

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

0

おそらく私は上記の答えから何かを逃しましたが、カプセル化における別の重要な概念は実装の隠蔽です。プライベートデータメンバー(クラスの実装の詳細)へのアクセスを削減すると、後でコードを簡単に変更できます。フレンドがプライベートデータに直接アクセスする場合、実装データフィールド(プライベートデータ)に変更を加えると、そのデータにアクセスするコードが壊れます。アクセス方法を使用すると、これはほとんどなくなります。かなり重要だと思います。


-1

あなたは可能性が最も厳しいと最も純粋なOOPの原則を遵守し、任意のクラスのためのデータメンバも持っていないことを確認アクセサをすべてのオブジェクトがなるようにしなければならない、間接を介して行われ、それらに基づいて行動するための唯一の方法とそのデータについて知ることができる唯一のものであれば、メッセージ、つまり、メソッド。

ただし、C#にも内部可視性キーワードがあり、Javaにはいくつかのデフォルトのパッケージレベルのアクセシビリティがあります。C ++は、他のどのクラスと他のクラスだけがそれを見ることができる正確に指定することにより、クラスへの可視性の妥協を最小限に抑えることにより、実際にはOOPの理想に近づきます。

私は実際にはC ++を使用していませんが、C#にフレンドがいる場合は、実際に多く使用するアセンブリグローバル内部修飾子の代わりに使用します。.NETでの展開の単位は、ので、それは本当に、incapsulationを破壊しないアセンブリ。

ただし、クロスアセンブリフレンドメカニズムのように機能するInternalsVisibleTo Attribute(otherAssembly)があります。Microsoftはこれをビジュアルデザイナーアセンブリに使用します。


-1

友達は、コールバックにも役立ちます。コールバックを静的メソッドとして実装できます

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

where callbacklocalCallback内部的に呼び出し、にclientDataはインスタンスが含まれています。私の考えでは、

または...

class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}

これにより、友達はcppでcスタイルの関数として純粋に定義され、クラスを混乱させることがなくなります。

同様に、私がよく目にするパターンは、クラスのすべての本当にプライベートなメンバーを別のクラスに入れることです。これは、ヘッダーで宣言され、cppで定義され、Friendlyです。これにより、コーダーは、クラスの多くの複雑さと内部の作業をヘッダーのユーザーから隠すことができます。

ヘッダー:

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

下流がこのように見る必要がないものを隠すことが容易になります。


1
インターフェイスはこれを達成するためのよりクリーンな方法ではないでしょうか?MyFooPrivate.hを探している人を止めるにはどうすればよいですか?
JBRウィルキンソン2009

1
まあ、秘密を守るためにプライベートとパブリックを使用している場合、簡単に敗北することになります。「非表示」とは、MyFooのユーザーが実際にプライベートメンバーを表示する必要がないことを意味します。これに加えて、ABI互換性を維持することは有用です。_privateをポインターにすると、プライベート実装は、パブリックインターフェイスに触れることなく、必要なだけ変更できるため、ABI互換性が維持されます。
shash

あなたはPIMPLイディオムを参照しています。その目的は、あなたが言っているような追加のカプセル化ではありませんが、実装の詳細をヘッダーから移動するため、実装の詳細を変更してもクライアントコードの再コンパイルは強制されません。さらに、このイディオムを実装するためにfriendを使用する必要はありません。
weberc2 2015年

はい、そうです。その主な目的は、実装の詳細を移動することです。そこにいる友達は、パブリッククラス内のプライベートメンバーをプライベートまたはその逆に処理するのに役立ちます。
2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.