74

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

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

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

4

15 に答える 15

80

Bjarne Stroustrup によるセクション 11.5「The C++ Programming Language」では、通常のメンバー関数は次の 3 つのものを取得すると述べています。

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

friendは 1 しか得られません。

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

于 2010-02-23T00:08:38.050 に答える
47

質問は、プログラマーがクラスのどのインスタンスでも機能しない関数を導入する必要がある状況に対処しているようです(したがって、メンバー関数を選択する可能性があります)。したがって、この回答は、静的関数とフレンド フリー関数のどちらかを選択する次の設計状況に限定します。staticf()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
}

andのセマンティクスについて事前に何も知らなくても(これについては後で説明します)、この限られたシナリオには簡単な答えがあります。これには2つの理由があります。f()Astatic


一般的なアルゴリズム:

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

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

staticインターフェイスに関数を持つ 2 つ以上のクラスがある場合、そのようなクラスで汎用的にf()呼び出す 1 つの関数を記述できます。f()

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

冗長な宣言:

2 番目の理由は、 free 関数を名前空間に配置する場合f()、他の宣言を導入せずにその定義をクラス定義に直接インライン化することは許可されないためです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()Af()NN::f

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

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

f()insideの宣言と定義をN(一般的に) 結合できない理由は、f()が の内部にアクセスすることになっているAため、が定義されているAときに の定義を確認する必要があるためf()です。しかし、前に述べたように、f()の内部の宣言は、内部の対応する宣言が行わNれる前に見られる必要があります。これにより、 の宣言と定義を事実上分割せざるを得なくなります。friendAf()


セマンティックな考慮事項:

上記の 2 点は普遍的に有効ですが、言説の宇宙によって動かされるものを「の」または「その逆」にf()するstaticよりも、 「と宣言する」方が好ましい理由があります。friendA

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

一方、friend関数は、フレンドであるクラスの内部メンバーへのアクセスが許可されているにもかかわらず、クラスの定義に対して論理的に外部にあるアルゴリズムです。

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

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

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


味:

今与えられたもの以外の議論は純粋に好みの問題から生じると私は信じています: 実際、フリーfriendstaticメンバーのアプローチの両方で、クラスのインターフェースが何であるかを 1 つの場所 (クラスの定義) に明確に述べることができます。したがって、設計上は同等です(もちろん、上記の観察を法として)。

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

于 2013-02-14T14:05:56.140 に答える
13

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

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

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

于 2013-02-11T15:11:10.213 に答える
6

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

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

  • コールバック関数として
  • クラススコープのメンバーを操作する
  • ヘッダーファイルに列挙したくない定数データを取得するには
  • 于 2010-02-23T00:03:33.813 に答える
    5

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

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

    そして、これ(静的対友人)は、反対ではないので、どちらか一方を使用することの問題ではありません。

    于 2010-02-22T23:55:36.880 に答える
    2
    • 静的メンバーよりもフレンドを好む理由の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
      };
      
    于 2013-02-11T15:02:43.423 に答える
    2

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

    于 2010-02-23T00:09:43.970 に答える
    2

    静的関数は、1 つのクラスのメンバーにのみアクセスできます。次のコードで説明されているように、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 クラスの両方のデータにアクセスできます。

    于 2013-02-09T20:26:09.143 に答える
    1

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

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

    于 2010-02-22T23:54:18.097 に答える
    1

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

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

    于 2010-02-22T23:59:12.690 に答える
    1

    フレンド関数は継承できませんが、静的関数は継承できます。なので、スタティック関数とフレンド関数の両方で目的が達成できる場合は、それを継承するかどうかを考えてください。

    于 2016-10-17T12:52:01.073 に答える
    0

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

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

      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によって説明されているとおりです

    于 2010-02-23T00:08:03.187 に答える
    0

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

    これらのメソッドは静的になり、何度も呼び出されるため、すべてのオブジェクト内で異なる場所を宣言すると、(メモリの観点から) コストがかかりすぎます。これは例の助けを借りて明確にすることができます: クラスの名前がファクトであり、そのデータ メンバーが n (階乗が関係する整数を表す) であるとします。この場合、静的として find_factorial() を宣言するのが賢明な決定です!!

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

    これで、次の質問で明確になりました..

    フレンド機能を使うときは?静的関数はいつ使用されますか?

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

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

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

    これがお役に立てば幸いです!!

    于 2013-02-14T23:31:15.633 に答える
    -1

    これが私が思うことです:

    フレンド機能-別のクラスメンバーにアクセスする必要があるが、クラスが関連していない場合。静的関数-「this」ポインタにアクセスする必要がない場合。でも、まだまだある気がします…。

    于 2010-02-22T23:55:59.163 に答える
    -2
    1. 静的データ メンバーは常にメモリを共有します。
    2. 静的データメンバーを使用できるのは静的関数だけです。
    3. 静的メンバー関数は、クラス名で呼び出すことができます。
    4. クラスで静的メンバーまたはメンバー関数のオブジェクトを作成するときは、クラスの外部で定義する必要があります。値を自動的に初期化します。
    5. 常にキーワード static を使用していました。
    6. 静的メンバーは、すべてのオブジェクトで共有できます。
    7. データ メンバーの型とスコープ、およびメンバー関数がクラス外です。
    8. 静的メンバー変数は、クラスの外で定義する必要があります。
    于 2016-10-12T01:41:41.100 に答える