#pragma曾经是一个安全的包含守卫吗?

我读到过,在使用#pragma once时,有一些编译器优化,可以导致更快的编译。我知道这是不标准的,因此可能会带来跨平台兼容性问题。

非windows平台(gcc)上的大多数现代编译器都支持这一点吗?

我希望避免平台编译问题,但也希望避免备用保护的额外工作:

#pragma once
#ifndef HEADER_H
#define HEADER_H


...


#endif // HEADER_H

我应该担心吗?我还需要在这上面花费更多的精力吗?

219374 次浏览

我不知道有什么性能上的好处,但它确实有效。我在我所有的c++项目中都使用它(当然我使用的是MS编译器)。我发现它比使用更有效

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

它做同样的工作,并且不使用额外的宏填充预处理器。

GCC正式支持#pragma once

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

我还发现,至少在GCC上,它识别标准#ifndef包含保护并优化它,所以它不应该比#pragma once慢很多。

使用'#pragma once'可能没有任何效果(并非所有地方都支持它——尽管它得到了越来越广泛的支持),所以无论如何都需要使用条件编译代码,在这种情况下,为什么要麻烦使用'#pragma once'呢?编译器可能会优化它。不过,这确实取决于您的目标平台。如果你所有的目标都支持它,那么就去使用它吧——但这应该是一个有意识的决定,因为如果你只使用这个pragma,然后移植到一个不支持它的编译器上,一切都会变得一团糟。

我希望#pragma once(或类似的东西)已经在标准中。包含警卫并不是什么大问题(但向学习该语言的人解释它们似乎有点困难),但这似乎是一个可以避免的小麻烦。

事实上,由于99.98%的情况下,#pragma once行为是理想的行为,如果编译器能自动处理防止多重包含头文件的问题,使用#pragma或其他东西来允许双重包含,那就更好了。

但是我们有我们所拥有的(除了你可能没有#pragma once)。

性能的好处在于,一旦读取了#pragma,就不必重新打开文件。使用保护,编译器必须打开文件(这可能会浪费时间)来获得它不应该包含它的内容的信息。

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

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

最后,你能得到更好的建议要确保从编译器获得最佳速度,而不必检查每个编译器的行为,在这种情况下,是一次性使用pragma和守卫。

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once


#include "Thing.h"


namespace MyApp
{
// ...
}


#endif

这样你就可以获得两者的优势(跨平台和帮助编译速度)。

由于打字时间较长,我个人使用一种工具来帮助生成所有这些信息(Visual Assist X)。

在非常大的树上使用gcc 3.4和4.1(有时使用distcc),我还没有看到使用#pragma一次代替或与标准include守卫结合使用时的速度有任何提高。

我真的不认为它值得让旧版本的gcc甚至其他编译器感到困惑,因为它并没有真正的节省。我并没有尝试过所有的去污工具,但我敢打赌它会让很多人感到困惑。

我也希望它能早点被采用,但我能看到“当ifndef工作得很好时,我们为什么需要它?”的争论。考虑到C语言的许多黑暗角落和复杂性,包含警卫是最简单的、自我解释的事情之一。如果你对预处理器的工作方式有一点了解,它们应该是不言自明的。

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

我使用它,我很高兴,因为我必须键入更少,使一个新的标题。它在Windows、Mac和Linux三个平台上都运行得很好。

我没有任何性能信息,但我相信,与解析c++语法的缓慢相比,#pragma和include守卫之间的差异将是微不足道的。这才是真正的问题。例如,尝试用c#编译器编译相同数量的文件和行,看看有什么不同。

最后,使用guard或pragma根本不重要。

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

如今,老式的include守卫就像#pragma一样快。即使编译器没有特别对待它们,当它看到#ifndef WHATEVER和WHATEVER被定义时,它仍然会停止。现在打开一个文件非常便宜。即使有改进,也只是毫秒级的改进。

我只是不使用#pragma一次,因为它没有任何好处。为了避免与其他include守卫发生冲突,我使用了如下代码:CI_APP_MODULE_FILE_H——> CI =公司首字母;APP =应用程序名称;其余部分不言自明。

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

也就是说,现在的编译器(包括GCC)足够聪明,可以像对待pragma一样对待include守卫。例如,他们不打开文件,避免文件IO惩罚。

在不支持pragma的编译器中,我见过手动实现有点麻烦。

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

我个人喜欢#pragma once方法,因为它避免了命名冲突和潜在的拼写错误的麻烦。相比之下,它的代码也更加优雅。也就是说,对于可移植的代码,如果编译器不同意,那么两者都使用也无妨。

不总是正确的。

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

GCC从3.4开始支持#pragma once,参见http://en.wikipedia.org/wiki/Pragma_once获得进一步的编译器支持。

我认为使用#pragma once而不是包含守卫的最大好处是可以避免复制/粘贴错误。

让我们面对这个问题:我们大多数人很少从头开始一个新的头文件,而只是复制一个现有的头文件并根据我们的需要进行修改。使用#pragma once创建工作模板要比包含守卫容易得多。对模板的修改越少,遇到错误的可能性就越小。在不同的文件中使用相同的include守卫会导致奇怪的编译器错误,并且需要一些时间来找出哪里出了问题。

#pragma once更容易使用。

如果我们使用msvc或Qt(到Qt 4.5),因为GCC(到3.4),msvc都支持#pragma once,我可以看到没有理由不使用#pragma once

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

对于那些认为总是希望自动地一次性包含头文件的人,还要补充一点:几十年来,我一直使用两次或多次包含头文件来构建代码生成器。特别是对于协议库存根的生成,我发现拥有一个非常可移植且功能强大的代码生成器非常舒服,无需额外的工具和语言。我不是唯一的开发人员使用这个方案这个博客是X-Macros显示。如果没有缺失的自动保护,这是不可能做到的。

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

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




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

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