为什么要将 #include 放在 include guards 之前?

5

在头文件中,将#include指令放在包含保护之前,是否有任何有效的理由,就像这样:

#include "jsarray.h"
#include "jsanalyze.h"
#include "jscompartment.h"
#include "jsgcmark.h"
#include "jsinfer.h"
#include "jsprf.h"
#include "vm/GlobalObject.h"

#include "vm/Stack-inl.h"

#ifndef jsinferinlines_h___
#define jsinferinlines_h___

//main body mostly inline functions 

#endif

请注意,这个例子是从一个真实的高知名度的开源项目中提取出来的,该项目应该由经验丰富的程序员开发 - Mozilla Spidermonkey 开源 Javascript 引擎,在 Firefox 10 中使用(同样的头文件也存在于最新版本中)。
在高知名度的项目中,我期望其设计背后必须有一些有效的原因。有哪些有效的理由可以解释为什么要在包含守卫之前使用 #include?这种模式适用于仅内联函数的头文件吗?还要注意,这个头文件 (jsinferinlines.h) 实际上是通过最后的 #include "vm/Stack-inl.h" (此头文件包括很多其他头文件之一,其中一个头文件再次包含了 jsinferinlines.h)指令自身包含的,在包含守卫之前,这对我来说甚至更没有意义。
2个回答

4
因为你期望这些头文件有自己的包含保护,所以实际上并没有什么区别。
另外请注意,这个头文件(jsinferinlines.h)实际上是通过最后一个#include "vm/Stack-inl.h" (这个头文件包含了很多其他头文件,其中一个再次包含了jsinferinlines.h)指令来包含它本身的,这对我来说甚至更加没有意义。
这不会产生任何影响,因为包含该文件的工作流程将是:
- 包含所有头文件直到"vm/Stack-inl.h" - 包含"vm/Stack-inl.h"直到最后一个#include "jsinferinlines.h"(你的文件) - 再次包含文件jsinferinlines.h(跳过所有之前的包含,因为有包含保护) - 到达#include "vm/Stack-inl.h"之后 - 最后从#ifndef jsinferinlines_h___开始处理
但总的来说,相互头文件递归是不好的,应该尽量避免。

1
如果我们有两个相互包含的文件怎么办?(我这里不讨论这种做法有多糟糕)。在使用 include guards 的正常情况下,当它们是文件中的第一件事时,什么也不会发生。如果 include guards 之前有 includes,则编译器将进入无限循环。 - Wojtek Surowka
@WojtekSurowka,当然。不过这是你的错误。 - Shoe
@berkus,所有现代编译器都会跟踪它们所包含的文件。 - Shoe
1
@Jeffrey - 现代编译器如果一个文件包含了自身会怎么处理? - Wojtek Surowka
2
@WojtekSurowka:现代编译器将检测常见的“包含保护”模式,并相应地优化对fopen的调用。合法使用包含的情况下,应该像未进行优化一样工作(否则就是一个错误),并且应该检测到错误(无限循环等),否则就是一个错误。当然,现代版本的gcc和clang应该能够处理这些边缘情况。 - Matthieu M.
显示剩余6条评论

1
在过去,SpiderMonkey头文件中存在许多包含循环,将头文件保护放在顶部会导致难以理解的编译错误。我认为将头文件保护放在包含语句下面只是对清理包含语句的一小步。

虽然我无法告诉您确切的包含序列会导致什么影响。

现在,在SM头文件中不应该存在任何包含循环,并且它们的样式是由Nicholas Nethercote编写的Python脚本强制执行的。如果您今天查看jsinferinlines.h,您会看到头文件保护位于其应在的顶部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接