web-dev-qa-db-ja.com

静的メンバー関数ではなく、フレンド関数をどこで使用しますか?

クラスのプライベートメンバーにアクセスする場合は、非メンバー関数をクラスのフレンドにします。これにより、静的メンバー関数と同じアクセス権が付与されます。どちらの方法でも、そのクラスのインスタンスに関連付けられていない関数が提供されます。

フレンド機能はいつ使用する必要がありますか?静的関数はいつ使用する必要がありますか?両方が問題を解決するための実行可能なオプションである場合、それらの適合性をどのように比較検討しますか?デフォルトで優先されるものはありますか?

たとえば、プライベートコンストラクタのみを持つクラスfooのインスタンスを作成するファクトリを実装する場合、そのファクトリ関数はfooの静的メンバーである必要があります(foo::create()を呼び出します) )それともフレンド関数にする必要がありますか(create_foo()を呼び出します)?

61
Swapna

Bjarne Stroustrupによる11.5節「C++プログラミング言語」では、通常のメンバー関数は3つのものを取得すると述べています。

  1. クラスの内部へのアクセス
  2. クラスのスコープ内にある
  3. インスタンスで呼び出す必要があります

friendsは1のみを取得します。

static関数は1と2を取得します。

69
pm100

質問は、プログラマがクラスの任意のinstanceに対してnot機能する関数を導入する必要がある状況に対処しているようです(したがって、staticメンバー関数を選択できる可能性があります)。したがって、この回答は、静的関数f()とフレンドフリー関数f()のどちらかを選択する次の設計状況に限定します。

_struct A
{
    static void f();     // Better this...
private:
    friend void f();  // ...or this?
    static int x;
};

int A::x = 0;

void A::f() // Defines static function
{
    cout << x;
}

void f() // Defines friend free function
{
    cout << A::x;
}

int main()
{
    A::f(); // Invokes static function
    f();    // Invokes friend free function
}
_

f()Asemanticsについて事前に何も知らずに(後でこれに戻ります) )、この限られたシナリオには簡単な答えがあります:static関数が望ましい。これには2つの理由があります。


一般的なアルゴリズム:

主な理由は、次のようなテンプレートを記述できるためです。

_template<typename T> void g() { T::f(); }
_

インターフェイスにstatic関数f()が含まれる2つ以上のクラスがある場合、これにより、そのようなクラスでf()を包括的に呼び出す単一の関数を作成できます。

f()を無料の非メンバー関数にした場合、同等のジェネリック関数を作成する方法はありません。 f()構文を名前空間に入れてN::f()構文を使用してA::f()構文を模倣できることは事実ですが、それでも不可能です名前空間名は有効なテンプレート引数ではないため、上記のg<>()などのテンプレート関数を記述します。

冗長宣言:

2番目の理由は、無料の関数f()を名前空間に配置するif場合、そのインラインを許可されないためです。 f()の他の宣言を導入せずに、クラス定義で直接定義:

_struct A
{
    static void f() { cout << x; } // OK
private:
    friend void N::f() { cout << x; } // ERROR 
    static int x;
};
_

上記を修正するには、クラスAの定義の前に次の宣言を置きます。

_namespace N
{
    void f(); // Declaration of f() inside namespace N
}

struct A
{
    ...
private:
    friend void N::f() { cout << x; } // OK
    ...
};
_

ただし、これは、f()を1か所で宣言および定義するという私たちの意図に反します。

さらに、f()を名前空間に保持しながらf()を個別に宣言して定義する場合でも、クラス定義の前にf()の宣言を導入する必要がありますfor A:これに失敗すると、_N::f_が名前の前にf()を名前空間N内で宣言する必要があるという事実についてコンパイラーが不満を言うでしょう合法的に使用されます。

したがって、ここではf()を2つだけではなく3つ別々の場所で宣言します(宣言と定義)。

  • Nの定義の前の名前空間A内の宣言。
  • friendの定義内のA宣言。
  • 名前空間N内のf()の定義。

N内のf()の宣言と定義を(一般的に)結合できない理由は、f()Aの定義は、f()が定義されているときに確認する必要があります。ただし、前述したように、A内の対応するN宣言が行われる前に、friend内のf()の宣言を確認する必要があります。これにより、宣言とf()の定義を効果的に分割することが強制されます。


意味上の考慮事項:

上記の2つの点は普遍的に有効ですが、Astaticまたはfriendにするよりも、f()Aとして宣言する方がよい理由があります。ディスコースの宇宙によって駆動されるその逆。

明確にするために、クラスのメンバー関数は、それがstaticでも非staticでも、論理的にの一部であることを強調することが重要ですそのクラス。それはその定義に貢献し、したがってそれの概念的な特徴付けを提供します。

一方、friend関数は、それがフレンドであるクラスの内部メンバーへのアクセスが許可されているにもかかわらず、論理的にexternalをクラスの定義に追加します。

関数は複数のクラスのfriendにすることができますが、1つだけのメンバーにすることもできます

したがって、特定のアプリケーションドメインでは、設計者は、前者をfriendにするかメンバーにするかを決定するときに、関数とクラスの両方のsemanticsを考慮に入れることができます。後者(これはstatic関数だけでなく、他の言語の制約が介入する可能性があるstatic関数にも適用されます)。

関数は論理的にクラスおよび/またはその動作を特徴付けるのに貢献しますか、それともむしろ外部アルゴリズムですか?この質問は、特定のアプリケーションドメインの知識がないと答えられません。


TASTE:

私が与えたもの以外の議論は純粋に趣味の問題から生じていると私は信じています:実際に無料のfriendstaticのメンバーアプローチはクラスのインターフェイスが1つのスポット(クラスの定義)になることを明確に示すため、設計上は同等です(もちろん、上記の観察を法として)。

残りの違いは文体的です:関数を宣言するときにstaticキーワードとfriendキーワードのどちらを書きたいのか、そして定義時に_A::_クラススコープ修飾子を書きたいのか_N::_名前空間スコープ修飾子ではなくクラス。したがって、これについてはこれ以上コメントしません。

41
Andy Prowl

その違いは、クラスと関数の関係の意図を明確に表現しています。

2つのnrelatedクラス間またはクラスと関数間の強い結合と特別な関係を意図的に示す場合は、friendを使用します。

関数がメンバーである論理的にはクラスの一部である場合、staticメンバー関数を使用します。

11
Alok Save

フレンド関数(およびクラス)は、クラスのプライベートおよび保護されたメンバーにアクセスできます。フレンド関数またはクラスを使用するための良いケースはめったにありません。一般的には避けてください。

静的関数は静的データ(つまり、クラススコープのデータ)にのみアクセスできます。クラスのインスタンスを作成せずに呼び出すことができます。静的関数は、クラスのすべてのインスタンスを同じように動作させたい場合に最適です。あなたはそれらを使うことができます:

  • コールバック関数として
  • クラススコープのメンバーを操作する
  • ヘッダーファイルで列挙したくない定数データを取得する
  • 5
    thebretness

    静的関数は、クラスのすべてのインスタンスで同じ関数が必要な場合に使用されます。このような関数は「this」ポインターにアクセスできないため、非静的フィールドにアクセスできません。これらは、クラスをインスタンス化せずに使用できる関数が必要な場合によく使用されます。

    フレンド関数とは、クラスに含まれていない関数であり、クラスのプライベートメンバーにアクセス権を与える必要があります。

    そして、これは(静的vs友達)反対ではないので、一方を他方を使用する問題ではありません。

    4
    synepis

    標準では、operator =()[]および->がメンバーであり、クラス固有である必要があります
    演算子new、new []、deleteおよびdelete []は静的メンバーである必要があります。状況が
    関数を呼び出すためにクラスのオブジェクトを必要としない場所で発生し、次に
    静的な関数。他のすべての機能:
    関数に演算子=()[]および->がストリームI/Oに必要な場合、
    または、左端の引数で型変換が必要な場合、またはクラスのパブリックインターフェイスのみを使用して実装できる場合は、メンバーにしない(最初の2つのケースで必要な場合はフレンド)
    仮想的に動作する必要がある場合、
    仮想メンバー関数を追加して、仮想動作を提供します
    そしてそれに関して実装する
    そうしないと
    メンバーにします。

    2
    Jagannath
    • 静的メンバーよりもフレンドを好む1つの理由は、関数をアセンブリ(または他の言語)で記述する必要がある場合です。

      たとえば、.cppファイルでextern "C"フレンド関数を常に宣言できます。

      class Thread;
      extern "C" int ContextSwitch(Thread & a, Thread & b);
      
      class Thread
      {
      public:
          friend int ContextSwitch(Thread & a, Thread & b);
          static int StContextSwitch(Thread & a, Thread & b);
      };
      

      そして後でアセンブリで定義されます:

                      .global ContextSwitch
      
      ContextSwitch:  // ...
                      retq
      

      技術的に言えば、静的メンバー関数を使用してこれを行うことができますが、名前のマングリング( http://en.wikipedia.org/wiki/Name_mangling )のため、アセンブリでの定義は簡単ではありません。

    • もう1つの状況は、演算子をオーバーロードする必要がある場合です。オーバーロードオペレーターは、友達または非静的メンバーを通じてのみ実行できます。演算子の最初の引数が同じクラスのインスタンスでない場合、非静的メンバーも機能しません。友達が唯一の選択肢になります:

      class Matrix
      {
          friend Matrix operator * (double scaleFactor, Matrix & m);
          // We can't use static member or non-static member to do this
      };
      
    2
    nav

    静的関数はoneクラスのメンバーにのみアクセスできます。 Friend関数は、次のコードで説明されているように、いくつかのクラスにアクセスできます。

    class B;
    class A { int a; friend void f(A &a, B &b); };
    class B { int b; friend void f(A &a, B &b); };
    void f(A &a, B &b) { std::cout << a.a << b.b; }
    

    f()は、AクラスとBクラスの両方のデータにアクセスできます。

    2
    tp1

    静的関数は継承できますが、フレンド関数は継承できません。したがって、静的関数とフレンド関数の両方で目的を達成できる場合は、それを継承するかどうかを考えます。

    1
    UserXYZ

    関数がクラスの特定のインスタンスの状態を読み取るまたは変更する必要がない場合(つまり、メモリ内のオブジェクトを変更する必要がない場合)、または関数ポインターを使用する必要がある場合は、静的関数を使用します。クラスのメンバー関数。この2番目のインスタンスで、常駐オブジェクトの状態を変更する必要がある場合は、thisを渡してローカルコピーを使用する必要があります。最初の例では、特定のタスクを実行するロジックがオブジェクトの状態に依存していないような状況が発生する可能性がありますが、論理的なグループ化とカプセル化では、特定のクラスのメンバーになります。

    クラスのメンバーではなく、クラスのメンバーであってはならないが、プライベート/保護されたカプセル化メカニズムを回避する正当な目的があるコードを作成した場合、フレンド関数またはクラスを使用します。これの1つの目的は、いくつかの共通データを必要とする2つのクラスがまだあり、ロジックを2回コーディングしていないことです。実際、私がこれまでにコーディングしたクラスのおそらく1%だけでこの機能を使用しました。ほとんど必要ありません。

    1
    San Jacinto

    フレンド関数は、他のクラスのプライベートおよび保護されたメンバーにアクセスできます。それらがプライベートまたはパブリックであるすべてのデータ天気にアクセスするために使用できることを意味します。そのため、静的メソッドができないデータにアクセスするためにフレンド関数が使用されます。

    これらのメソッドは静的にされるため、何度も呼び出されるため、すべてのオブジェクト内で別の場所を宣言すると、メモリの点で非常にコストがかかります。これは例を使用して明らかにすることができます:クラスの名前がfactであり、そのデータメンバーがn(階乗が関係する整数を表す)であるとします。

    これらは、コールバック関数として使用され、クラススコープのメンバーを操作して、ヘッダーファイルで列挙したくない定数データを取得します

    今、私たちは以下の質問で明確です。

    フレンド機能を利用する場合は?静的関数を使用する場合?

    現在、両方が問題を解決するための実行可能なオプションである場合、アクセシビリティ(プライベートデータのアクセシビリティ)とメモリ効率の観点から、それらの適合性を強化できます。デフォルトでは、より良いメモリ管理が必要な状況が多く、データの範囲が気になる場合があるため、誰も優先されることはありません。

    例:小さな時間のインスタンスごとにcreate()メソッドを呼び出す必要があり、data(Private data)のスコープに関心がない場合、foo :: create()はcreate_foo()よりも優先されます。

    また、複数のクラスのプライベート情報を取得したい場合は、foo :: create()よりもcreate_foo()が優先されます。

    これがあなたのお役に立てば幸いです!

    0
    newbieprog

    静的関数は、thisにアクセスできない関数です。

    フレンド関数は、クラスのプライベートメンバーにアクセスできる関数です。

    0
    tzenes

    静的関数はさまざまな方法で使用できます。

    たとえば、単純なファクトリ関数として:

      class Abstract {
      private:
        // no explicit construction allowed
        Abstract(); 
        ~Abstract();
    
       public:
         static Abstract* Construct() { return new Abstract; }
         static void Destroy(Abstract* a) { delete a; }
       };
    
       ...
       A* a_instance = A::Conctruct();
       ...
       A::Destroy(a_instance);
    

    これは非常に単純化された例ですが、私が何を意味するのか説明したいと思います。

    または、クラスで動作するスレッド関数として:

     class A {
    
     public:
        static void worker(void* p) {
                A* a = dynamic_cast<A*>(p);
                do something wit a;
        }   
     } 
    
     A a_instance;
     pthread_start(&thread_id, &A::worker, &a_instance);
     .... 
    

    友人は完全に別の話であり、彼らの用法はthebretnessで説明されているとおりです

    0
    lollinus