【问题标题】:Specify a class member function as a friend of another class?将一个类成员函数指定为另一个类的朋友?
【发布时间】:2012-05-19 18:10:02
【问题描述】:

根据《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() 成为class A 的朋友,而不是整个class B。但是上面的代码产生了一个错误:'B' : is not a class or namespace name。 (我使用的是 Visual C++ 2005)

【问题讨论】:

    标签: c++ friend


    【解决方案1】:

    尝试将 B 定义放在 A 之前:

    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 的完整定义。但是B需要知道A,但不需要完整的定义,所以需要A的前向声明。

    【讨论】:

    • 但是如果在 fB(A& a) 我使用 a 来访问 A 中的变量,例如 a.variable;这将是非法的,因为 A 尚未定义。
    • @ipkiss 是的,因为如果你在头类声明中需要完整的定义,那么你将需要完整的定义。但是如果你在一个单独的实现文件中这样做,你可以包含 A 的完整声明。
    【解决方案2】:

    当编译器开始编译代码时(通常从顶部开始)并遇到这一行:

    friend void B::fB(A& a);
    
    1. 此时,编译器不知道 B 的类型信息,因此会引发错误(“B”:不是类或命名空间名称)。

    2. 通过类 B 的前向声明,编译器在其与所有成员的实际声明之前就知道 B 的类型是 Class。

    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 的类型而不是 A 的成员,因此 A 的前向声明解析步骤 4。

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

    【讨论】:

    • @BrunoMartinez - 没有完美的解决方案,因为两者都周期性地依赖于彼此的完整定义。我给出这个特殊案例的目的是强调在软件设计中解耦实体的重要性,并在处理这个问题时更清楚地了解 Friend 功能。我想,您可能已经意识到,可能的选择是创建一个共同的朋友功能
    【解决方案3】:

    为此,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(){}
    };
    

    【讨论】:

      【解决方案4】:

      当编译器开始读取代码时(通常从顶部开始)并遇到这一行:

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

      那么编译器不明白这个B:: 是什么意思。即使您稍后在代码中定义了此类,但编译器并不知道。因此,如果定义位于代码的后面,那么练习前向声明类(class Name;)通常是一个好习惯。

      【讨论】:

        【解决方案5】:

        首先向前声明A类,使其在B类定义中可见。然后定义包含类 B 的友元函数的类 A。

        【讨论】:

          【解决方案6】:

          首先,在使用特定的类名之前,您必须先声明它。因此,在最初声明 B 类之前,您需要在 A 类中使用 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;
          }
          

          【讨论】:

            【解决方案7】:

            @juanchopanza @ipkiss 关于由于 A 尚未定义而无法在 fB(A& a) 中访问 A 的数据成员的问题。不用在单独的文件中定义它并包含它,您可以在定义类 A 之后定义函数 fB(A& a) 以便 fB(A& a) 能够看到 A 的数据成员。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-08-29
              • 2016-11-26
              • 2015-11-24
              • 2012-06-07
              • 1970-01-01
              • 1970-01-01
              • 2016-09-05
              • 1970-01-01
              相关资源
              最近更新 更多