【问题标题】:friend function and include loop友元函数和包含循环
【发布时间】:2015-11-06 21:59:04
【问题描述】:

我遇到了两个类之间的友元函数问题。让我们看一些代码:

头等舱:

#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" 来解决这个问题,但随后它将是包含循环。你知道怎么做吗?

【问题讨论】:

  • #include 循环没有问题,而不是当您使用 #ifndef _FIRST_H_ 启动它们时
  • @MadsMarquart,不正确。这将是一个问题。
  • 另外,我不明白你为什么要这样做?以这种方式检索字符串似乎毫无用处。
  • @RSahu 抱歉,不知道。
  • @MadsMarquart 这是我另一个问题的简化

标签: c++ friend-function friend-class


【解决方案1】:

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

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

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

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

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

但它会是包含循环。

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

知道怎么做吗?

如果你想使用friend void Second::fun()作为友元声明,声明和定义的顺序很重要,需要对你的类稍作修改。

  1. 声明类First
  2. fun()声明(不是定义)定义类Second
    • 您现在不能使用First fObj 作为成员或尝试使用new First,因为First 尚未定义且First 的构造函数目前未知。 指针或参考就可以了。
    • 由于使用了指针或引用,**构造函数也应该修改。
  3. fun()的朋友声明定义类First
  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; }

【讨论】:

    【解决方案2】:

    你知道怎么做吗?

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

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

    class First
    {
      public:
        First();
        ~First();
    
        std::string const& getString() const { return str; }
    
      private:
        std::string str = "Dziala\n";
    };
    

    【讨论】:

    • 为什么?如果我可以使用friend CLASS,那为什么我不能使用friend FUNCTION机制呢?
    • @QueUe,根据我的经验,使用friend 机制是最后的手段——不管你是创建一个类还是一个函数friend。这是类之间非常紧密耦合的症状。通过使Second 成为First 的朋友,您使SecondFirst 的内部紧密耦合。 First 不能在没有咨询 Second 的情况下随意更改其内部结构。
    • 不幸的是,这是我编程课程的作业。我必须使用朋友功能:)
    • @QueUe,您是否需要将成员函数设为 friend 作为任务的一部分?你知道你根本无法做你正在尝试的事情。仔细阅读作业要完成的任务,您可能会找到解决方案。
    • 是的,它是必需的。这很奇怪,因为我可以在这里使用朋友类但不能使用朋友功能。顺便说一句,谢谢你:)
    【解决方案3】:

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

    class Second     // complete class definition, not just a forward declaration
    {
        public:
    
           void fun();
    };
    
    class First
    {
        friend void Second::fun();
    };
    

    这样做的结果是First 不能简单地成为Second 的成员。为了接受这一点,编译器需要对First 的定义具有Second 的完整定义的可见性才能进行编译,而且还需要对Second 的定义具有First 的完整定义的可见性才能进行编译。那是一种无限递归的依赖,这往往会使编译器感到不安。

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

    所以,这样就可以了

    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 的朋友并完成它。毕竟,将一个类的一个成员函数声明为友元会断言该成员函数将始终按预期工作。在极少数情况下,可以信任类的单个成员函数按要求工作,但不信任同一类的其他成员函数。

    【讨论】:

    • 在过去 24 小时内,我的回答似乎被其他成员提出并拒绝了一系列编辑。我最后的编辑实际上是由 Chao Mai 提出的,以纠正几个错误,因为出于某种原因,我无法自行决定接受(我本来可以)或拒绝它们。
    猜你喜欢
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多