コンパイラがコードのコンパイルを開始し(通常は上から)、次の行が発生した場合:
friend void B::fB(A& a);
この時点で、コンパイラはBの型情報を認識していないため、エラーがスローされます('B':はクラス名または名前空間名ではありません)。
クラスBの前方宣言により、コンパイラーは、すべてのメンバーによる実際の宣言よりも前に、Bのタイプがクラスであることを認識します。
クラスBの前方宣言の後、以下のコードを実行します。
///////////////
class B;
class A
{
public:
friend void B::fB(A& a);
void fA(){};
};
class B
{
public:
void fB(A& a){};
void fB2(A& a){};
};
それでもエラー!!!
前方宣言は、プログラマーがまだ完全な定義を与えていない識別子の宣言にすぎないためです。したがって、コンパイラはクラスAの前にBの完全な定義を必要とします。
注:クラスAの定義はBのタイプとBの定義(つまりB :: fB)に依存するため、前方宣言だけでは解決できません。クラスBの完全な定義はクラスAの前に定義する必要があります。
4このコードを実行します
////////
class B
{
public:
void fB(A& a){};
void fB2(A& a){};
};
class A
{
public:
friend void B::fB(A& a);
void fA(){}
};
それでもエラー!!!
クラスBのメンバー関数fBおよびfB2はタイプAの引数を持っていますが、コンパイラーはAのタイプ情報を認識しないため、クラスAの前方宣言により、コンパイラーにAのタイプ情報を通知できます。注:クラスBの定義はにのみ依存します。 AのメンバーではなくAのタイプであるため、Aの前方宣言はステップ4を解決します。
- 最終コード
////////////////////////
class A; // forward declaration of A needed by B
class B
{
public:
void fB(A& a);
};
class A
{
int i;
public:
friend void fA(A& a); //specifying function fA as a friend of A, fA is not member function of A
friend void B::fB(A& a); //specifying B class member function fB as a friend of A
};
// fA is Friend function of A
void fA(A& a)
{
a.i = 11; // accessing and modifying Class A private member i
cout<<a.i<<endl;
}
// B::fB should be defined after class A definition only because this member function can access Class A members
void B::fB(A& a)
{
a.i = 22; // accessing and modifying Class A private member i in Class B member function fB
cout<<a.i<<endl;
}
int main()
{
A a;
fA(a); // calling friend function of class A
B b;
b.fB(a); // calling B class member function fB, B:fB is friend of class A
return 0;
}
6演習:
// Cyclic dependency
#include<iostream>
using namespace std;
class A;
class B
{
public:
void fB(A& a);
friend void A::fA(B& b); //specifying class A's member function fA as a friend of B
};
class A
{
int i;
public:
void fA(B& b);
friend void B::fB(A& a); //specifying class B's member function fB as a friend of A
};
int main()
{
return 0;
}