Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 友元函数错误:';B';尚未申报_C++ - Fatal编程技术网

C++ 友元函数错误:';B';尚未申报

C++ 友元函数错误:';B';尚未申报,c++,C++,我测试了下面的代码(只是为了测试,我知道这是个坏主意),但我不明白为什么它会出错。请帮帮我 有两个类A和B,都在单独的hpp文件中。我让它们相互包含(我认为这并不重要,因为包含保护),直到我在B.cpp中添加了B::fun的定义,它才出现问题 A.h \ifndef A\u H #定义一个 #包括“B.h” 甲级{ 朋友B::乐趣(A&); }; #恩迪夫 B.h #ifndef B#H #定义B_H #包括“A.h” 甲级; B类{ 公众: 虚无乐趣(A&); }; #恩迪夫 B.cpp

我测试了下面的代码(只是为了测试,我知道这是个坏主意),但我不明白为什么它会出错。请帮帮我

有两个类
A
B
,都在单独的
hpp
文件中。我让它们相互包含(我认为这并不重要,因为
包含保护
),直到我在
B.cpp
中添加了
B::fun
的定义,它才出现问题

A.h

\ifndef A\u H
#定义一个
#包括“B.h”
甲级{
朋友B::乐趣(A&);
};
#恩迪夫
B.h

#ifndef B#H
#定义B_H
#包括“A.h”
甲级;
B类{
公众:
虚无乐趣(A&);
};
#恩迪夫
B.cpp

#包括“B.h”
虚空B::乐趣(A&A){
}
main.cpp

#包括“A.h”
#包括“B.h”
int main(){
}
我使用如下命令运行它:

g++-g-o测试main.cpp B.cpp
错误消息

A.h:7:17: error: 'B' has not been declared
    friend void B::fun(A&);
我知道症状的两种解决方法,但我想了解问题的根本原因

方法1: 注释代码
#在
B.h

方法2: 将
#包含“B.h”
修改为
#在
B.cpp中包含“A.h”


我想知道为什么这些变通方法可以避免这些症状,这两种方法的确切原因。

只需从
B.hpp
中删除
#包括“A.hpp”
。这是因为当
B.cpp
包括
B.hpp
时,后者在定义
B
之前依次包括
A.hpp
。但是您根本不需要包含
A.hpp
,因为您已经正确地向前声明了
A类

您没有要求解决方案,您要求的原因是您的两个解决方法消除了错误,这是一个很好的学习方式。我的尊敬

为了理解为什么会出现错误,以及为什么可以用自己找到的两种方法消除错误,您应该“玩预处理器”并自己动手,同时观察
ifdef
s(也称为重新包含卫士)的效果

查看B.cpp是如何编译的,插入执行include操作的头文件

你会得到:


#ifndef B_H
#define B_H

#ifndef A_H
#define A_H

#include "B.h" /* not expanded, will be removed next step */

class A {
    friend void B::fun(A&);
};

#endif

class A;

class B {
    public:
        void fun(A&);
};
#endif


void B::fun(A& a) {
    
}
然后移除那些被
#ifdef
s阻止的部分,即第二次B头时,您会得到:

#ifndef B_H
#define B_H

#ifndef A_H
#define A_H

class A {
    friend void B::fun(A&); /* obviously unknown B */
};

#endif

class A;

class B {
    public:
        void fun(A&);
};
#endif


void B::fun(A& a) {
    
}
现在您了解了为什么在B.cpp中出现错误。
为什么方法1有效

应用更改后,让我们再次执行相同的操作:


#ifndef B_H
#define B_H

/* #include "A.h" */ /* method 1 */

class A;

class B {
    public:
        void fun(A&); /* fine, because forward declaration three lines above is sufficient */
};
#endif
void B::fun(A& a) {
    
}

现在,为什么方法2有帮助


/* method 2, include A.h before B.h into B.cpp */
#ifndef A_H
#define A_H

#ifndef B_H
#define B_H

/* #include "A.h" not expanded because of ifdef */

class A;

class B {
    public:
        void fun(A&); /* fine, because of forward declaration */
};
#endif

class A {
    friend void B::fun(A&); /* fine of forward declaration B having been seen */
};

#endif

/* #include "B.h" not expanded, because of ifdef */

void B::fun(A& a) {
    
}

简而言之,这两种解决方法都是在使用
A
之前查看正向声明,在使用
B
之前完全声明。

要了解错误的来源,只需遵循编译
B.cpp
时发生的“包含链”:

  • B.cpp
    包括
    B.h
  • B.h
    包括
    A.h
  • A.h
    尝试包含
    B.h
    ,但没有,因为您已正确设置了包含防护
  • 现在编译器来到了第行
    friendvoidb::fun(A&)
    A.h
    中,但在此阶段
    B
    尚未声明
    我编辑这篇文章是为了强调“知道如何解决”和“在没有理解的情况下采取变通办法”之间的区别。我认为这使问题更加清楚。如果你不喜欢它,请随意撤消我的编辑,我将离开它。祝你好运。第一个“解决方法”是要走的路(事实上是删除而不是评论;-))。第二个问题其实只是一个解决办法,让问题重新出现以获得额外的文件。突然,我想到了另一个问题。main.cpp->main.obj和B.cpp->B.obj,它们都有B类。为什么没有错误:链接它们时B的多个定义?如果你能告诉我,我会很感激的。一个有趣的问题。请使用“提问”按钮创建它。