質問は、プログラマーがクラスのどのインスタンスでも機能しない関数を導入する必要がある状況に対処しているようです(したがって、メンバー関数を選択する可能性があります)。したがって、この回答は、静的関数とフレンド フリー関数のどちらかを選択する次の設計状況に限定します。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
}
andのセマンティクスについて事前に何も知らなくても(これについては後で説明します)、この限られたシナリオには簡単な答えがあります。これには2つの理由があります。f()
A
static
一般的なアルゴリズム:
主な理由は、次のようなテンプレートを記述できるためです。
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()
A
f()
N
N::f
したがって、2 つ (宣言と定義) だけでなく、 3 つのf()
別々の場所で言及することになります。
- の定義の
N
前の名前空間内の宣言。A
- の定義内の宣言
friend
。A
f()
名前空間内の定義N
。
f()
insideの宣言と定義をN
(一般的に) 結合できない理由は、f()
が の内部にアクセスすることになっているA
ため、が定義されているA
ときに の定義を確認する必要があるためf()
です。しかし、前に述べたように、f()
の内部の宣言は、内部の対応する宣言が行わN
れる前に見られる必要があります。これにより、 の宣言と定義を事実上分割せざるを得なくなります。friend
A
f()
セマンティックな考慮事項:
上記の 2 点は普遍的に有効ですが、言説の宇宙によって動かされるものを「の」または「その逆」にf()
するstatic
よりも、 「と宣言する」方が好ましい理由があります。friend
A
static
明確にするために、クラスのメンバー関数は、それが であるかどうかにかかわらず、static
論理的にそのクラスの一部であるという事実を強調することが重要です。それはその定義に貢献し、したがってそれの概念的な特徴付けを提供します。
一方、friend
関数は、フレンドであるクラスの内部メンバーへのアクセスが許可されているにもかかわらず、クラスの定義に対して論理的に外部にあるアルゴリズムです。
関数はfriend
複数のクラスに属することができますが、メンバーになることができるのは 1 つの だけです。
したがって、特定のアプリケーション ドメインでは、設計者は関数とクラスの両方のセマンティクスを考慮して、前者を後者のメンバーにするか、後者のメンバーにするかを決定する必要がある場合があります (これは、関数friend
だけでなく、非クラスにも適用されます)。 static
-static
他の言語の制約が介在する可能性がある場合にも機能します)。
関数は、クラスおよび/またはその動作を特徴付けるために論理的に貢献していますか、それとも外部アルゴリズムですか? この質問は、特定のアプリケーション ドメインに関する知識がなければ答えられません。
味:
今与えられたもの以外の議論は純粋に好みの問題から生じると私は信じています: 実際、フリーfriend
とstatic
メンバーのアプローチの両方で、クラスのインターフェースが何であるかを 1 つの場所 (クラスの定義) に明確に述べることができます。したがって、設計上は同等です(もちろん、上記の観察を法として)。
残りの違いは文体です:関数を宣言するときにstatic
キーワードまたはキーワードを書きたいかどうか、およびクラスを定義するときに名前空間スコープ修飾子ではなくクラススコープ修飾子を書きたいかどうか。したがって、これについてはこれ以上コメントしません。friend
A::
N::