我试图理解为什么两个相互包含的标头(每个标头包括另一个标头)不能按您预期的方式工作。我想知道我的推理是否正确:
如果你有 FooHeader.h:
#pragma once
#include "BarHeader.h"
struct Foo { Bar bar;};
还有一个 BarHeader.h:
#pragma once
#include "FooHeader.h"
struct Bar { Foo foo; };
可能无法编译的原因如下:
假设您有 main.cpp:
#include "BarHeader.h"
int main() { }
现在预处理器完成它的工作,替换“BarHeader.h”,使其看起来像这样:
|remove|
#include "BarHeader.h"
|add|
#include "FooHeader.h"
struct Bar { Foo foo; };
int main() {}
好的,这是第 1 步。但是预处理器还有工作要做,要替换“FooHeader.h”,所以下一步是:
|remove|
#include "FooHeader.h"
|add|
#include "BarHeader.h"
struct Foo { Bar bar;}
struct Bar { Foo foo; }
int main() {}
现在文件看起来像这样:
#include "BarHeader.h"
struct Foo { Bar bar;}
struct Bar { Foo foo; }
int main() {}
它仍然有一个包含,但是,由于该头文件已经被包含,所以它不再包含它,所以最终结果是:
struct Foo { Bar bar;}
struct Bar { Foo foo; }
int main() {}
这就是编译出现问题的原因吧?
如果预处理器只是将已包含的标头移动到文件中最高/第一次出现的位置,这是否就可以解决?所以像“我发现我已经把你包括在内,但你已经被包括在这里,所以我将它移到这里,然后一切都很好。”?这样就可以解决问题了吧?
#include "BarHeader.h"
int main() { }
成为
// #include "BarHeader.h"
#pragma once // BarHeader
#include "FooHeader.h"
struct Bar { Foo foo; };
int main() { }
然后我们扩展#include指令
// #include "BarHeader.h"
#pragma once // BarHeader
// #include "FooHeader.h"
#pragma once // FooHeader
#include "BarHeader.h"
struct Foo { Bar bar;};
struct Bar { Foo foo; };
int main() { }
现在我们扩展
#include "BarHeader.h"
- 这不会执行任何操作,因为 #pragma once "BarHeader.h"
已经被评估,并且我们包含了 'BarHeader.h"
。
// #include "BarHeader.h"
#pragma once // BarHeader
// #include "FooHeader.h"
#pragma once // FooHeader
// #include "BarHeader.h"
struct Foo { Bar bar;};
struct Bar { Foo foo; };
int main() { }
我们得到一个错误。
您的规则“将其移至最早的位置”毫无意义 - 当我们遇到另一个 BarHeader.h
包含时,我们已经
已经通过了
BarHeader.h
。 你无法“倒回”它。
处理它的技巧很容易理解。
此外,您的具体示例无法通过任何标头顺序来解决。 您不能在类 B 中包含类 A,因为 C++ 中的类是该类的实际实例而不是对该类的引用(如在 C#/Java/等中)。
预声明规则实际上是为了帮助避免循环结构定义。