web-dev-qa-db-ja.com

クラスメンバー関数を別のクラスのフレンドとして指定しますか?

C++ Primerの本によると、著者は、クラス全体ではなく、別のクラスのフレンドとしてクラスメンバー関数を指定できると述べています(634ページ)。

次に、このコードをテストしました。

class A
{
public:
    friend void B::fB(A& a);
    void fA(){}
};
class B
{
public:
    void fB(A& a){};
    void fB2(A& a){};
};

FB()をクラスB全体ではなく、クラスAのフレンドにしたかっただけです。しかし、aboutコードでエラーが発生しました:'B' : is not a class or namespace name。 (私はVisual C++ 2005を使用しています)

16
ipkiss

Aの前にBの定義を入れてみてください。

class A; // forward declaration of A needed by B

class B
{
public:
    // if these require full definition of A, then put body in implementation file
    void fB(A& a); // Note: no body, unlike original.
    void fB2(A& a); // no body.
};

class A
{
public:
    friend void B::fB(A& a);
    void fA(){}
};

Aには、Bの完全な定義が必要です。ただし、BAについて知る必要がありますが、完全な定義は必要ないため、Aの前方宣言が必要です。

18
juanchopanza

コンパイラがコードのコンパイルを開始し(通常は上から)、次の行が発生した場合:

friend void B::fB(A& a);
  1. この時点で、コンパイラはBの型情報を認識していないため、エラーをスローします( 'B':はクラス名または名前空間名ではありません)。
  2. クラスBの前方宣言により、コンパイラーは、すべてのメンバーによる実際の宣言よりも前に、Bのタイプがクラスであることを認識します。

  3. クラス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の前方宣言がステップ4を解決するように、AのメンバーではなくAのタイプ。

  1. 最終コード

////////////////////////

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;
}
4

これが機能するためには、Bの定義の前にAの完全な定義を知っている必要があります。

したがって、Aは完全な型を必要としないため、前方宣言Bを宣言し、定義を次のように切り替えます。

class A;
class B
{
public:
    void fB(A& a){};
    void fB2(A& a){};
};
class A
{
public:
    friend void B::fB(A& a);
    void fA(){}
};
3
Luchian Grigore

コンパイラがコードの読み取りを開始し(通常は上から)、次の行が発生した場合:

friend void B::fB(A& a);

その場合、コンパイラはこのB::の意味を理解しません。コードの後半でこのクラスを定義したが、コンパイラーはそれを認識していません。したがって、定義がコードの後半にある場合は、通常、class(class Name;)の前方宣言を行うことをお勧めします。

2
AbhimanyuAryan

まず、特定のクラス名を使用する前に、まずそれを宣言する必要があります。したがって、クラスBが最初に宣言される前に、クラスAでクラスBを使用しているため、クラスBの前方宣言が必要になります。

次に、両方のクラスを定義した後、関数(両方のクラスの変数を使用している-ここではフレンド関数)を定義する必要があります。そうしないと、エラーが発生する可能性があります。

例えば

#include<iostream>

using namespace std;

class alpha1;

class alpha2
{
    public:

        void put_bata(int a,int b);
};

void alpha2 :: put_bata(int a,int b)
{
    alpha1 net;

    net.roll=a;

    net.id=b;

    net.get_data();
}

class alpha1
{
    int roll;

    int id;

    public:

        void get_data(void)
        {
            cout<<roll<<endl<<id<<endl;
        }

        friend void alpha2 :: put_bata(int a,int b);
};

int main()
{
    alpha2 gamma;

    gamma.put_bata(5,6);

    return 0;
}

クラスの前方宣言があったとしても、put_bataが定義される前にrollとidにアクセスしようとするとエラーが表示されますが、以下に示すコードは問題なく機能します。

#include<iostream>

using namespace std;

class alpha1;

class alpha2
{
    public:

        void put_bata(int a,int b);
};

class alpha1
{
    int roll;

    int id;

    public:

        void get_data(void)
        {
            cout<<roll<<endl<<id<<endl;
        }

        friend void alpha2 :: put_bata(int a,int b);
};

void alpha2 :: put_bata(int a,int b)
{
    alpha1 net;

    net.roll=a;

    net.id=b;

    net.get_data();
}


int main()
{
    alpha2 gamma;

    gamma.put_bata(5,6);

    return 0;
}
0
New_Bee

最初にクラスAを前方宣言して、クラスBの定義に表示されるようにします。次に、クラスBのフレンド関数を含むクラスAを定義します。

0
abdul rizwan

@juanchopanza @ipkiss Aがまだ定義されていないため、fB(A&a)内のAのデータメンバーにアクセスできないという問題について。別のファイルで定義して含める代わりに、クラスAの定義後に関数fB(A&a)を定義して、fB(A&a)がAのデータメンバーを表示できるようにすることができます。

0
mrboieng