フレンド宣言で最初に導入されるフレンド関数またはフレンド・クラスの名前は、フレンド関係を認可する
クラス (囲みクラスとも呼ばれます) のスコープ内にはなく、フレンド関係を認可する
クラスのメンバーでもありません。
フレンド宣言で最初に導入された関数の名前は、囲みクラスが含まれている最初の非クラス・スコープのスコープ内にあります。 フレンド宣言で与えられる関数の本体は、クラス内で定義されるメンバー関数と同じ方法で処理されます。 定義の処理は、最外部の囲みクラスの終わりまで開始されません。 さらに、関数定義の本体内の修飾されない名前による検索は、その関数定義が入っているクラスから始められます。
フレンド宣言で最初に宣言されるクラスは、extern 宣言と同等です。次に例を示します。
class B {}; class A { friend class B; // global class B is a friend of A };
フレンド・クラスの名前が、フレンド宣言よりも前に導入されている場合、コンパイラーは、その フレンド・クラスの名前と一致するクラス名の検索を、フレンド宣言のスコープの先頭から開始します。 ネスト・クラスの宣言の後に同じ名前のフレンド・クラスの宣言が続いている場合、そのネスト・クラスは、 囲みクラスのフレンドです。
フレンド・クラス名のスコープは、最初の非クラスの囲みスコープです。 次に例を示します。
class A { class B { // arbitrary nested class definitions friend class C; }; };
は、以下と同等です。
class C; class A { class B { // arbitrary nested class definitions friend class C; }; };
フレンド関数が別のクラスのメンバーである場合には、スコープ・レゾリューション演算子 (::) を 使用する必要があります。 次に例を示します。
class A { public: int f() { } }; class B { friend int A::f(); };
基底クラスのフレンドは、その基底クラスから派生したクラスには、継承されません。 次の例は、このことを示しています。
class A { friend class B; int a; }; class B { }; class C : public B { void f(A* p) { // p->a = 2; } };C は A のフレンドから継承していますが、クラス C が クラス A のフレンドではないので、コンパイラーは、ステートメント p->a = 2 を 許可しません。
フレンド関係は、移行できません。 次の例は、このことを示しています。
class A { friend class B; int a; }; class B { friend class C; }; class C { void f(A* p) { // p->a = 2; } };C は A のフレンドのフレンドですが、クラス C が クラス A のフレンドではないので、コンパイラーは、ステートメント p->a = 2 を 許可しません。
ローカル・クラスにフレンドを宣言し、フレンドの名前が非修飾である場合、コンパイラーは、最も内側にある 囲みの非クラス・スコープ内でのみ名前を検索します。 関数を宣言してから、ローカル・スコープのフレンドとして関数を宣言する必要があります。 クラスでこれを実行する必要はありません。 しかし、フレンド・クラスの宣言は、囲みスコープ内の、同じ名前のクラスを隠します。 次の例は、このことを示しています。
class X { }; void a(); void f() { class Y { }; void b(); class A { friend class X; friend class Y; friend class Z; // friend void a(); friend void b(); // friend void c(); }; ::X moocow; // X moocow2; }
上記の例では、コンパイラーは、次のステートメントを認めます。
コンパイラーは、次のステートメントを許可しません。
関連参照