1

我在两个班级之间遇到了朋友功能的问题。让我们看一些代码:

一级:

#ifndef _FIRST_H_
#define _FIRST_H_

//#include "Second.h"
#include <string>

class Second;
class First
{
    friend void Second::fun();

    std::string str = "Dziala\n";
public:
    First();
    ~First();
};
#endif

和二等:

#ifndef _SECOND_H_
#define _SECOND_H_

#include<iostream>
#include "First.h"

class Second
{
    First fObj;
public:
    Second();
    ~Second();
    void fun() { std::cout << fObj.str; }
};
#endif 

如果我尝试交朋友 CLASS 是没有问题的。如果我像上面的例子一样交朋友 FUNCTION,就会出现问题。我可以通过头等舱中的#include "Second.h" 来解决这个问题,但随后它将是包含循环。你知道怎么做吗?

4

3 回答 3

2

如果我像上面的例子一样交朋友 FUNCTION,就会出现问题。

// #include "Second.h"
#include <string>

class Second;
class First
{
    friend void Second::fun();
...

第一行是 class 的声明Second。这是一个前向声明。对于 class Second,在其声明之后和定义之前,它是不完整的类型。所以Second被称为类类型,但它包含的成员是未知的。所以你不能在void Second::fun()这里使用会员。

friend class Second工作正常,因为它从不尝试使用不完整类型的成员。

但随后它将是包含循环。

正如 MadsMarquart 所说,这不是问题,因为你已经有了头球后卫。

知道怎么做吗?

如果您想friend void Second::fun()用作友元声明,则声明和定义的顺序很重要,并且需要对您的类进行一些修改。

  1. 声明类First
  2. Second使用的声明(不是定义)定义类fun()
    • 您现在不能使用 aFirst fObj作为成员或尝试使用new First,因为First尚未定义并且First现在的构造函数是未知的。指针或引用会很好。
    • 由于使用了指针或引用,因此还应修改 ** 构造函数。
  3. First使用 的朋友声明定义类fun()
  4. 定义fun().

根据您的示例修改的代码,

class First;

class Second {
 public:
  Second(First& rfObj) : fObj(rfObj) {}
  void fun();

 private:
  First& fObj;
};

class First {
  friend void Second::fun();
 public:
  First() = default;

private:
  std::string str = "Dziala\n";
};

void Second::fun() { std::cout << fObj.str; }
于 2016-02-08T09:36:50.183 回答
0

你知道怎么做吗?

您不能使用该friend机制来做您正在尝试的事情。

一个简单的解决方案是通过成员函数公开First::str而不用担心friend构造。从长远来看,这是一个更清洁的解决方案。

class First
{
  public:
    First();
    ~First();

    std::string const& getString() const { return str; }

  private:
    std::string str = "Dziala\n";
};
于 2015-11-06T22:06:42.293 回答
0

除非完整的类定义可见,否则不可能将成员函数声明为友元。否则将允许任意代码声明和定义该类的创建者不打算的类的成员。

class Second     // complete class definition, not just a forward declaration
{
    public:

       void fun();
};

class First
{
    friend void Second::fun();
};

这样做的结果是First不能简单地成为Second. 要接受这一点,编译器需要对要编译Second的定义First的完整定义具有可见性,而且还要对要编译的First定义的完整定义具有可见性Second。这是一个无限递归的依赖,它往往会扰乱编译器。

只能用前向声明声明的类成员(或实际上通常是变量)的唯一类型是指针或引用。

所以,这会工作

class First;

class Second     // complete class definition, not just a forward declaration
{
    private:
        First &fObj;    // note this is a reference
    public:

       void fun();

       Second();
       ~Second();
};

class First
{
    friend void Second::fun();
};

Second::Second() : fObj(*(new First))   // assumes First has appropriate (not shown) constructor
{}

Second::~Second()
{
   delete &fObj;
}

但是,请注意,除非 的定义事先对编译器可见,Second否则也无法编译的构造函数和析构函数。First这是因为不可能仅基于前向声明创建或销毁类类型的实例(即与原始问题相同的原因)。

实际上,我只是声明类Second是它的朋友First并完成它。毕竟,将一个类的一个成员函数声明为友元会断言该成员函数将始终按预期工作。在极少数情况下,可以信任类的单个成员函数按要求工作,但不能信任同一类的其他成员函数。

于 2016-02-08T10:22:11.300 回答