C++友元类和友元成员函数

4

我正在学习C++类中的友元函数、友元类和友元成员函数;现在,以下代码可以很好地编译:

#include <iostream>

class A 
{
public:
    friend class B;
    //friend void B::set(int i);
    //friend int B::get();
    friend int function(A a);
    A(int i);
    void set(int i);
    int get();
private:
    int i;
};

A::A(int i) : i(i)
{
}

void A::set(int i)
{
    this->i = i;
}

int A::get()
{
    return i;
}

class B
{
public:
    B(A a);
    void set(int i);
    int get();
private:
    A a;
};

B::B(A a) : a(a)
{
}

void B::set(int i)
{
    a.i = i;
}

int B::get()
{
    return a.i;
}

int function(A a);

int main(int argc, char *argv[])
{
    A a(0);
    std::cout << "in A i=" << a.get() << std::endl;
    a.set(10);
    std::cout << "in A i=" << a.get() << std::endl;
    B b(a);
    std::cout << "in B i=" << b.get() << std::endl;
    b.set(21);
    std::cout << "in B i=" << b.get() << std::endl;
    std::cout << "function returns " << function(a) << std::endl;
}

int function(A a)
{
    return a.i;
}

我可以在不提前声明类B或函数“function”的情况下向类A中的“function”授予友元关系。 现在,如果我想要向类B中的这两个成员函数授予友元关系,则必须在定义类A之前定义类B,否则将无法实现:

#include <iostream>

class B;   // doesn't work, incomplete type (complete type needed)

class A 
{
public:
    //friend class B;
    friend void B::set(int i);
    friend int B::get();
    friend int function(A a);
    A(int i);
    void set(int i);
    int get();
private:
    int i;
};

A::A(int i) : i(i)
{
}

void A::set(int i)
{
    this->i = i;
}

int A::get()
{
    return i;
}

B::B(A a) : a(a)
{
}

void B::set(int i)
{
    a.i = i;
}

int B::get()
{
    return a.i;
}

int function(A a);

int main(int argc, char *argv[])
{
    A a(0);
    std::cout << "in A i=" << a.get() << std::endl;
    a.set(10);
    std::cout << "in A i=" << a.get() << std::endl;
    B b(a);
    std::cout << "in B i=" << b.get() << std::endl;
    b.set(21);
    std::cout << "in B i=" << b.get() << std::endl;
    std::cout << "function returns " << function(a) << std::endl;
}

int function(A a)
{
    return a.i;
}

但我无法在定义B类之前定义A类,所以我卡住了。即使是向前声明(而不是定义)B类也不起作用。

因此我的问题是:

1)为什么在友元声明中我不需要向前声明函数或整个类,但如果我需要指定该类的成员函数,则需要定义类? 我知道友元声明并不是常规意义上的声明(它们只授予访问权,而不向前声明任何内容)。

2)除了在B中声明A成员对象为A *a,我如何使我的代码编译通过?


1
实际上,没有必要将类B的成员函数声明为友元函数,因为类A具有公共接口。 - Vlad from Moscow
@VladfromMoscow,这确实是一个人为制造的例子。我只是想了解为什么它不起作用以及如何绕过它。 - Luca
@Luca 这不起作用是因为编译器不知道类B是否确实具有这些成员函数。 - Vlad from Moscow
1
@n.m. 实际上,这是有意义的。 - Luca
类是授权的一个小单位。当然,您可以使用更细粒度的访问权限,有时这很好,但这并不会决定您的设计成败。如果您将整个类设置为友元,也不会发生什么不好的事情。 - n. m.
显示剩余4条评论
2个回答

1

这是一个友元类的示例以及如何使用它。这个示例来自cplusplus.com。我发布这篇文章的原因是因为你的示例并没有真正说明在c++中友元的正确使用方式。我希望这可以让你了解何时/为什么应该使用友元,并且这可以帮助你解决前向声明问题。

// friend class
#include <iostream>
using namespace std;

class Square;

class Rectangle {
    int width, height;
  public:
    int area ()
      {return (width * height);}
    void convert (Square a);
};

class Square {
  friend class Rectangle;
  private:
    int side;
  public:
    Square (int a) : side(a) {}
};

void Rectangle::convert (Square a) {
  width = a.side;
  height = a.side;
}

int main () {
  Rectangle rect;
  Square sqr (4);
  rect.convert(sqr);
  cout << rect.area();
  return 0;
}

在这个例子中,类Rectangle是类Square的友元,允许Rectangle的成员函数访问Square的私有和保护成员。更具体地说,Rectangle访问描述正方形边长的成员变量Square::side。
在这个例子中还有一些新东西:程序开头有一个空的Square类声明。这是必要的,因为类Rectangle使用Square(作为成员convert的参数),而Square使用Rectangle(将其声明为友元)。
友元关系从不自动对应:在我们的例子中,Square将Rectangle视为友元类,但Rectangle不将Square视为友元类。因此,Rectangle的成员函数可以访问Square的受保护和私有成员,但反过来则不行。当然,如果需要,Square也可以声明为Rectangle的友元,以授予这种访问权限。
友元关系的另一个特性是它们不是传递性的:朋友的朋友除非明确指定,否则不被视为朋友。

0

只需将B类设置为A类的友元即可。这样做不会产生任何负面影响。

如果您绝对想要更加精细地控制,可以使用代理类来实现(使用您的示例进行简化):

class Proxy;

class A
{
public:
    friend class Proxy;
    A(int i);
private:
    int i;
};

class B
{
public:
    B(A a);
    void set(int i);
    int get();
private:
    A a;
};

class Proxy
{
  friend void B::set(int);
  friend int B::get();
  static int& get_i(A& a) { return a.i; }
  static const int& get_i(const A& a) { return a.i; }
};

现在你可以在B::setB::get(仅限这两个函数)中使用Proxy::get_i(a)代替a.i


1
在这种情况下,我甚至不需要Proxy类的前向声明。 - Luca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接