次の方法で共有


friend (C++)

場合によっては、クラスで、クラスのメンバーでない関数や別のクラスのすべてのメンバーに対してメンバーレベルのアクセスを許可すると便利です。 これらの無料の関数とクラスは "フレンド" と呼ばれ、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 番目の形式では新しいクラスは導入されません。この形式は、クラスが既に宣言されている場合に使用できます。また、テンプレート型パラメーターまたは typedeffriend として宣言するときに使用する必要があります。

参照型がまだ宣言されていない場合は、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 へのアクセスは、クラス AFunc1 では正しく、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 つのクラス宣言 (BaseDerivedaFriendanotherFriend) を示しています。 aFriend のプライベート メンバー (および Base が継承した可能性があるすべてのメンバー) に直接アクセスできるのは、Base クラスだけです。

フレンド リレーションシップの派生的影響を示す図。

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

インラインの friend 定義

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

関連項目

キーワード