#pragma曾经安全包括后卫吗?

时间:2009-04-24 20:50:35

标签: c++ include-guards

我读过使用#pragma once时有一些编译器优化,这可以加快编译速度。我认为这是非标准的,因此可能会造成跨平台兼容性问题。

这是非Windows平台(gcc)上大多数现代编译器支持的东西吗?

我想避免平台编译问题,但也希望避免后备警卫的额外工作:

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

我应该担心吗?我是否应该为此花费更多精力?

15 个答案:

答案 0 :(得分:306)

#pragma once确实有一个缺点(除非是非标准的),如果你在不同的位置有相同的文件(我们有这个因为我们的构建系统复制文件)然后编译器会认为这些是不同的文件。

答案 1 :(得分:160)

使用#pragma once应该适用于任何现代编译器,但我认为没有任何理由不使用标准#ifndef包含守护程序。它工作得很好。需要注意的是,GCC在version 3.4之前不支持#pragma once

我还发现,至少在GCC上it recognizes the standard #ifndef include guard and optimizes it,所以它不应该比#pragma once慢得多。

答案 2 :(得分:56)

我希望#pragma once(或类似的东西)符合标准。包括警卫不是一个真正的大问题(但他们似乎有点难以向学习该语言的人解释),但这似乎是一个可以避免的轻微烦恼。

事实上,由于99.98%的时间,#pragma once行为是期望的行为,如果编译器自动处理阻止多个包含头的情况会很好,{{1}或者允许双重包含的东西。

但我们拥有的东西(除了你可能没有#pragma)。

答案 3 :(得分:32)

我不知道任何性能优势,但它确实有效。我在所有的C ++项目中使用它(授予我使用MS编译器)。我发现它比使用

更有效
#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

它执行相同的工作,并且不会使用其他宏填充预处理器。

GCC正式支持#pragma once as of version 3.4

答案 4 :(得分:21)

自3.4以来,GCC支持#pragma once,请参阅http://en.wikipedia.org/wiki/Pragma_once以获得进一步的编译器支持。

我在使用#pragma once而不是包含警卫时看到的巨大好处是避免复制/粘贴错误。

让我们面对现实:我们大多数人几乎不能从头开始新的头文件,而只是复制现有的头文件并根据我们的需要进行修改。使用#pragma once而不是包含警卫来创建工作模板要容易得多。我修改模板的次数越少,我就越不容易遇到错误。在不同的文件中包含相同的防护导致奇怪的编译器错误,并且需要一些时间来弄清楚出了什么问题。

TL; DR:#pragma once更容易使用。

答案 5 :(得分:11)

使用'#pragma once'可能没有任何影响(它在任何地方都不受支持 - 尽管它得到越来越广泛的支持),所以你需要使用条件编译代码,在这种情况下,为什么要打扰' #pragma once“?无论如何,编译器可能会优化它。但它确实依赖于您的目标平台。如果你的所有目标都支持它,那么继续使用它 - 但它应该是一个有意识的决定,因为如果你只使用pragma然后移植到不支持它的编译器,那么所有地狱都将破裂。

答案 6 :(得分:11)

我使用它并且我很满意它,因为我必须输入更少的东西来制作新的标题。它在三个平台上运行良好:Windows,Mac和Linux。

我没有任何性能信息,但我相信#pragma和include guard之间的区别与解析C ++语法的速度相比毫无结果。这是真正的问题。例如,尝试使用C#编译器编译相同数量的文件和行,以查看差异。

最后,使用警卫或编译,根本不重要。

答案 7 :(得分:5)

并非总是如此。

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566有一个很好的例子,两个文件都包含在内,但由于时间戳和内容相同(文件名不同),误认为是相同的。

答案 8 :(得分:4)

性能优势在于,一旦读取了#pragma,就不必重新打开文件。使用警卫,编译器必须打开文件(时间成本可能很高),以获取不应再包含其内容的信息。

这就是理论,因为对于每个编译单元,某些编译器将自动不会打开没有任何读取代码的文件。

无论如何,对于所有编译器来说都不是这样,所以理想情况下,跨平台代码必须避免使用#pragma,因为它根本不是标准的/没有标准化的定义和效果。然而,实际上,它确实比守卫好。

最后,better suggestion you can get确保从编译器获得最佳速度,而不必在这种情况下检查每个编译器的行为,是使用pragma一次和保护。

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

通过这种方式,您可以获得两者的最佳效果(跨平台和帮助编译速度)。

由于输入的时间较长,我个人使用一种工具来帮助以非常邪恶的方式生成所有内容(Visual Assist X)。

答案 9 :(得分:2)

在非常大的树上使用gcc 3.4和4.1(有时使用distcc),在使用#pragma代替或与标准包含警卫组合使用时,我还没有看到任何加速。 / p>

我真的不知道它的价值如何可能会混淆旧版本的gcc,甚至其他编译器,因为没有真正的节省。我没有尝试过所有各种各样的指针,但我愿意打赌它会让很多人感到困惑。

我也希望它早日被采用,但我可以看到“当ifndef工作得很好时,为什么我们需要它?”。考虑到C的许多黑暗角落和复杂性,包括守卫是最简单,自我解释的事情之一。如果您对预处理器的工作原理知之甚少,那么它们应该是自我解释的。

但是,如果您确实观察到显着的加速,请更新您的问题。

答案 10 :(得分:2)

今天老派的守卫和#pragma一样快。即使编译器没有特别处理它们,它仍然会在看到#ifndef WHATEVER并定义WHATEVER时停止。今天打开文件很便宜。即使有改进,它也会达到毫秒级。

我只是不使用#pragma一次,因为它没有任何好处。为了避免与其他包含警卫发生冲突,我使用如下内容:CI_APP_MODULE_FILE_H - > CI =公司缩写; APP =申请名称;其余的是不言自明的。

答案 11 :(得分:2)

主要区别在于编译器必须打开头文件才能读取包含保护。相比之下,pragma曾经导致编译器跟踪文件,并且当遇到同一文件的另一个包含时不执行任何文件IO。虽然这听起来可以忽略不计,但它可以很容易地扩展到大型项目,尤其是没有良好标题的项目包括学科。

那就是说,这些天编译器(包括海湾合作委员会)足够聪明,可以像pragma一样对待包括守卫。即他们不打开文件并避免文件IO惩罚。

在不支持pragma的编译器中,我看到手动实现有点麻烦..

#ifdef FOO_H
#include "foo.h"
#endif

我个人喜欢#pragma一次方法,因为它避免了命名冲突和潜在拼写错误的麻烦。相比之下,它也是更优雅的代码。也就是说,对于可移植代码来说,除非编译器抱怨它,否则不应该受到影响。

答案 12 :(得分:1)

如果我们使用msvc或Qt(最高为Qt 4.5),由于GCC(最多3.4),msvc都支持#pragma once,我认为没有理由不使用#pragma once

源文件名通常是相同的类名,我们知道,有时我们需要 refactor 来重命名类名,那么我们也必须更改#include XXXX,所以我认为手册保持#include xxxxx不是一项聪明的工作。即使使用Visual Assist X扩展,维护“xxxx”也不是必要的工作。

答案 13 :(得分:1)

对于那些认为总是需要自动一次性包含头文件的人们的补充说明:几十年来,我使用两个或多个包含头文件来构建代码生成器。特别是对于协议库存根的生成,我发现拥有一个非常便携且功能强大的代码生成器而没有其他工具和语言非常舒适。我不是唯一使用此方案的开发人员this blogs X-Macros显示。如果没有缺少自动防护功能,这是不可能做到的。

答案 14 :(得分:1)

我使用 #ifndef/#define 包含守卫,使用包含 UUID 的符号,如下所示:

#ifndef ARRAY__H_81945CB3_AEBB_471F_AC97_AB6C8B220314
#define ARRAY__H_81945CB3_AEBB_471F_AC97_AB6C8B220314 /* include guard */


#endif

我一直使用能够自动生成 UUID 的编辑器。 这可以防止与来自其他库的具有相同基本名称的文件发生名称冲突,并检测完全相同的文件是否放置在文件系统内的多个位置。

缺点是表的大小增加了,因为符号要大得多,但我还没有看到它的问题。