場合によっては、クラスで、クラスのメンバーでない関数や別のクラスのすべてのメンバーに対してメンバーレベルのアクセスを許可すると便利です。 これらの無料の関数とクラスは "フレンド" と呼ばれ、friend
キーワードでマークされます。 クラスの実装側が自分のフレンドを宣言することだけが可能です。 関数またはクラスが自分自身をいずれかのクラスのフレンドとして宣言することはできません。 クラス定義では、friend
キーワードと、非メンバー関数または他のクラスの名前を使用して、クラスのプライベートおよびプロテクト メンバーへのアクセスを許可します。 テンプレート定義では、型パラメーターを friend
として宣言できます。
構文
friend-declaration
=
friend
function-declaration
friend
function-definition
friend
simple-type-specifier
;
friend
typename-specifier
;
friend
の宣言
以前に宣言されなかった friend
関数を宣言すると、その関数は外側の非クラス スコープにエクスポートされます。
friend
宣言で宣言された関数は、extern
キーワードを使って宣言されたものとして扱われます。 詳細については、extern
を参照してください。
グローバル スコープの関数はプロトタイプの前に friend
関数として宣言できますが、メンバー関数は、完全なクラス宣言の前に friend
関数として宣言することはできません。 次のコードは、このような宣言がどのように失敗するかを示しています。
class ForwardDeclared; // Class name is known.
class HasFriends
{
friend int ForwardDeclared::IsAFriend(); // C2039 error expected
};
前の例では、スコープにクラス名 ForwardDeclared
を入力しますが、宣言全体 (特に関数 IsAFriend
を宣言する部分) は不明です。 HasFriends
クラスに friend
宣言があると、エラーが生成されます。
C++ 11 では、クラスの friend 宣言には次の 2 つの形式があります。
friend class F;
friend F;
最初の形式では、その名前の既存のクラスが最も内側にある名前空間に見つからなかった場合に、新しいクラス F が導入されます。 C++11: 2 番目の形式では新しいクラスは導入されません。この形式は、クラスが既に宣言されている場合に使用できます。また、テンプレート型パラメーターまたは typedef
を friend
として宣言するときに使用する必要があります。
参照型がまだ宣言されていない場合は、friend class F
を使用します。
namespace NS
{
class M
{
friend class F; // Introduces F but doesn't define it
};
}
宣言されていないクラス型で friend
を使用すると、エラーが発生します。
namespace NS
{
class M
{
friend F; // error C2433: 'NS::F': 'friend' not permitted on data declarations
};
}
次の例では、friend F
は NS のスコープ外で宣言されている F
クラスを参照しています。
class F {};
namespace NS
{
class M
{
friend F; // OK
};
}
friend F
を使用して、テンプレート パラメーターをフレンドとして宣言します。
template <typename T>
class my_class
{
friend T;
//...
};
friend F
を使用して、typedef をフレンドとして宣言します。
class Foo {};
typedef Foo F;
class G
{
friend F; // OK
friend class F // Error C2371 -- redefinition
};
相互にフレンドである 2 つのクラスを宣言するには、2 番目のクラス全体が最初のクラスのフレンドとして指定される必要があります。 この制限の理由は、2 番目のクラスが宣言された位置でのみコンパイラは個々のフレンド関数を宣言するために十分な情報を得られるためです。
Note
2 番目のクラス全体は最初のクラスへのフレンドである必要がありますが、最初のクラスのどの関数が 2 番目のクラスのフレンドであるかを選択できます。
friend 関数
friend
関数は、クラスのメンバーではないが、クラスのプライベート メンバーとプロテクト メンバーにアクセスできる関数です。 friend 関数はクラス メンバーと見なされません。これらの関数は、特別なアクセス特権が付与されている標準の外部関数です。 フレンドはクラスのスコープ内に含まれません。また、別のクラスのメンバーでない限り、メンバー選択演算子 (. および ->) を使用して呼び出されません。 friend
関数はアクセスを付与しているクラスで宣言されます。 friend
宣言はクラス宣言内の任意の場所に配置できます。 アクセス制御キーワードによる影響はありません。
次に、Point
クラスと ChangePrivate
フレンド関数の例を示します。 friend
関数は、自身がパラメーターとして受け取る Point
オブジェクトのプライベート データ メンバーにアクセスできます。
// friend_functions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Point
{
friend void ChangePrivate( Point & );
public:
Point( void ) : m_i(0) {}
void PrintPrivate( void ){cout << m_i << endl; }
private:
int m_i;
};
void ChangePrivate ( Point &i ) { i.m_i++; }
int main()
{
Point sPoint;
sPoint.PrintPrivate();
ChangePrivate(sPoint);
sPoint.PrintPrivate();
// Output: 0
1
}
フレンドとしてのクラス メンバー
クラス メンバー関数は他のクラスのフレンドとして宣言できます。 次の例を確認してください。
// classes_as_friends1.cpp
// compile with: /c
class B;
class A {
public:
int Func1( B& b );
private:
int Func2( B& b );
};
class B {
private:
int _b;
// A::Func1 is a friend function to class B
// so A::Func1 has access to all members of B
friend int A::Func1( B& );
};
int A::Func1( B& b ) { return b._b; } // OK
int A::Func2( B& b ) { return b._b; } // C2248
この例では、A::Func1( B& )
関数に B
クラスへの friend
アクセスのみ許可されます。 したがって、プライベート メンバー _b
へのアクセスは、クラス A
の Func1
では正しく、Func2
では正しくありません。
friend
クラスは、すべてのメンバー関数が、クラスのその他のプライベート メンバーとプロテクト メンバーにアクセスできるメンバー関数を持つ、クラスの friend
関数であるクラスです。 friend
クラスの B
宣言が次のようになっていたとします。
friend class A;
その場合、A
クラスのすべてのメンバー関数に B
クラスへの friend
アクセスが許可されます。 次のコードは friend
クラスの例です。
// classes_as_friends2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class YourClass {
friend class YourOtherClass; // Declare a friend class
public:
YourClass() : topSecret(0){}
void printMember() { cout << topSecret << endl; }
private:
int topSecret;
};
class YourOtherClass {
public:
void change( YourClass& yc, int x ){yc.topSecret = x;}
};
int main() {
YourClass yc1;
YourOtherClass yoc1;
yc1.printMember();
yoc1.change( yc1, 5 );
yc1.printMember();
}
明示的に指定されていない限り、フレンド関係は相互関係ではありません。 上の例では、YourClass
のメンバー関数は YourOtherClass
のプライベート メンバーにアクセスできません。
マネージド型 (C++/CLI) には、friend
関数、friend
クラス、または friend
インターフェイスを含めることはできません。
フレンド関係は継承されません。つまり、YourOtherClass
から派生したクラスは YourClass
のプライベート メンバーにアクセスできません。 フレンド関係は推移的ではないため、YourOtherClass
のフレンドであるクラスは YourClass
にアクセスできません。
次の図は、4 つのクラス宣言 (Base
、Derived
、aFriend
、anotherFriend
) を示しています。 aFriend
のプライベート メンバー (および Base
が継承した可能性があるすべてのメンバー) に直接アクセスできるのは、Base
クラスだけです。
この anotherFriend クラスが、クラス aFriend をフレンドとするクラス ベースとの間でフレンドのリレーションシップを持たないことを示す図。 クラス aFriend はクラス Base によってフレンド化されていますが、クラス Derived が Base から継承している場合でも、クラス Derived とのフレンド リレーションシップはありません。 これは、継承が、派生クラスが基底クラスと同じフレンドを持つことを意味しないことを示しています。
インラインの friend
定義
フレンド関数は、クラス宣言内で (関数本体を指定して) 定義できます。 これらの関数はインライン関数です。 メンバーのインライン関数と同様に、すべてのクラス メンバーが発生した直後、クラス スコープが閉じる前に定義されているように動作します (クラス宣言の末尾)。 クラス宣言内で定義されているフレンド関数は、それを囲んでいるクラスのスコープ内にあります。