friend (C++)

状況によっては、クラスが、クラスのメンバーではない関数または別のクラス内のすべてのメンバーに対してメンバー レベルのアクセス権を付与すると便利です。 これらの無料の関数とクラスは、キーワード (keyword)によってマークされたfriendフレンド呼ばれます。 クラスの実装側が自分のフレンドを宣言することだけが可能です。 関数またはクラスは、クラスのフレンドとして自分自身を宣言することはできません。 クラス定義では、キーワード (keyword)と非メンバー関数またはその他のクラスの名前を使用friendして、クラスのプライベートメンバーと保護されたメンバーへのアクセスを許可します。 テンプレート定義では、型パラメーター friendを .

構文

friend-declaration:
friend function-declaration
friend function-definition
friendelaborated-type-specifier;;
friend simple-type-specifier ;
friend typename-specifier ;

friend 宣言

以前に宣言されていない関数を friend 宣言すると、その関数は外側の非クラス スコープにエクスポートされます。

宣言でfriend宣言された関数は、キーワード (keyword)を使用して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 では、クラスのフレンド宣言には次の 2 つの形式があります。

friend class F;
friend F;

最初の形式では、その名前の既存のクラスが最も内側にある名前空間に見つからなかった場合に、新しいクラス F が導入されます。 C++11: 2 番目のフォームは新しいクラスを導入しません。クラスが既に宣言されている場合に使用でき、テンプレート型パラメーターまたは typedef a として 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 宣言はクラス宣言内の任意の場所に配置できます。 アクセス制御キーワード (keyword)の影響を受けない。

次に、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 へのアクセスは、クラス AFunc1 では正しく、Func2 では正しくありません。

friendクラスは、メンバー関数がクラスの関数であるfriendクラス、つまりメンバー関数が他のクラスのプライベート メンバーと保護されたメンバーにアクセスできるクラスです。 friend クラスの B 宣言が次のようになっていたとします。

friend class A;

その場合、クラス内のすべてのメンバー関数にクラスABへのアクセスが許可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();
}

フレンドシップは、明示的に指定されていない限り相互に関係しません。 上記の例では、のメンバー関数 YourClassYourOtherClass.

マネージド型 (C++/CLI) には、関数、クラス、friendまたはfriendインターフェイスをfriend含めることはできません。

フレンドシップは継承されません。つまり、派生したクラスは YourOtherClass 'プライベート メンバー' にアクセス YourClassできません。 友情は推移的ではないので、友人 YourOtherClass であるクラスは 'プライベート メンバー' にアクセス YourClassできません。

次の図は、4 つのクラス宣言 (BaseDerivedaFriendanotherFriend) を示しています。 aFriend のプライベート メンバー (および Base が継承した可能性があるすべてのメンバー) に直接アクセスできるのは、Base クラスだけです。

A diagram that shows the derivation implications of a friend relationship.

この図は、anotherFriend クラスが、フレンド クラス aFriend クラスの基底クラスとフレンド関係を持っていないことを示しています。 クラス aFriend はクラス Base によってフレンド化されますが、派生クラスが Base から継承されている場合でも、クラス Derived とのフレンド関係はありません。 これは、継承が派生クラスに基底クラスと同じフレンドがあることを意味しないことを示しています。

インライン friend 定義

フレンド関数は、クラス宣言内で (関数本体を指定して) 定義できます。 これらの関数はインライン関数です。 メンバー インライン関数と同様に、すべてのクラス メンバーが表示された直後、クラス スコープが閉じられる前 (クラス宣言の最後) に定義されたかのように動作します。 クラス宣言内で定義されているフレンド関数は、それを囲んでいるクラスのスコープ内にあります。

関連項目

キーワード