我是C ++的新手,但我确实有一些Java经验。 编码时,我偶然发现了一个让我感到困惑的错误。 这是我的代码(简化,但错误是相同的):
A.H:
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
A.cpp:
#include "A.h"
#include <iostream>
A::A() {}
void A::foo() {
b.bar(this);
}
void A::sayHello() {
std::cout << "Hello" << std::endl;
}
B.h:
#pragma once
#include "A.h"
class B
{
public:
B();
void bar(A *a);
};
B.cpp:
#include "B.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
我想将 a 对象的指针传递给 B 中的 bar 函数,以便我能够修改和访问栏中 的字段。奇怪的是,当我通过另一个类的 A 实例调用 foo 时,我收到了这些错误:
1>------ Build started: Project: Test, Configuration: Debug Win32 ------
1> main.cpp
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A'
1> B.cpp
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C3646: 'b': unknown override specifier
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1> A.cpp
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A'
1>d:\stuff\visual studio 2015\projects\test\test\a.cpp(5): error C2660: 'B::bar': function does not take 1 arguments
1> Generating Code...
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
如果我不在 Bh 中包含 Ah 并且我没有将任何内容传递给栏功能。
我试图谷歌可能会导致这些错误,但我自己无法解决问题,因为我不明白导致这些错误的原因。我做错了什么?
答案 0 :(得分:2)
在 A.h 的标题文件中,您有:
#include "B.h"
在 B.h 的标题文件中,您有:
#include "A.h"
您有一个循环包含,其中A
和B
的定义相互依赖。
一个简单的解决方案是在class B
:
#include "A.h"
替换为class B;
#include "A.h"
醇>
请注意,您不能使用class A
的远期声明,原因是您b
的值class B
作为class A
的成员变量:
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b; /// I mean this line here more specifically
};
编译器需要知道class B
的定义才能确定A类的正确大小。这就是为什么你必须把#include "B.h"
放在 Ah的开头< / strong>即可。
答案 1 :(得分:2)
让我们看看B.cpp并观察包含的内容:
#include "B.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
用B.h粘贴代替我们获得的包含:
#include "A.h"
class B
{
public:
B();
void bar(A *a);
};
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
然后将A.h放在其中包括:
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
class B
{
public:
B();
void bar(A *a);
};
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
此处我们停止,因为pragma once
阻止重新加入B.h
我们可以看到,在A
类的定义中,我们有B b;
,但尚未定义类B
。 KABOOM。如果没有A
,则无法定义B
,而B
尚未定义。
A
的大小必须B
才能满足B b;
,因此需要B
的完整定义。 B
只需知道A
存在,因为它只需要指向A
的指针即可满足void bar(A *a);
。所以......
<强> A.H 强>
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
<强> A.cpp 强>
#include "A.h"
#include <iostream>
A::A() {}
void A::foo() {
b.bar(this);
}
void A::sayHello() {
std::cout << "Hello" << std::endl;
}
<强> B.h 强>
#pragma once
class A; // forward definition of class A
class B
{
public:
B();
void bar(A *a);
};
<强> B.cpp 强>
#include "A.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
答案 2 :(得分:1)
两个标头都互相引用。实际上,编译器只能实际评估一个,并且无法解析另一个头中对该类的引用。这个错误并不明显,但是&#34;一次&#34; #pragma正在使其中一个标题包含不按预期发生。
如果B.h头文件不需要知道关于A类的实现细节,在这种情况下它不是,那么不要在B.h中包含A.h文件。相反,将bar()的函数声明更改为:
bar( class A *a );
编译器可以从中构建代码,该代码将指针传递给A对象,而不知道A内部的内容。它不需要A.h头。
然后在B.cpp。
中的B.h头文件之后包含A.h头文件我对此进行了测试,它对我有用。
答案 3 :(得分:0)
进行以下更改:
B.h : - 转发声明A
#pragma once
// #include "A.h"
class A;
//^^^^^^
class B {
public:
B();
void bar(A *a);
};
B.cpp :#include "A.h"
//#include "B.h"
#include "A.h"
//^^^^^^^^^^^^
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
就是这样。