#pragma曾经是一名安全的守卫吗?
我读过#pragma once
使用#pragma once
时会有一些编译器优化,这会导致编译速度更快。 我认识到这是非标准的,因此可能会造成跨平台兼容性问题。
这是非Windows平台上的大多数现代编译器(gcc)所支持的吗?
我想避免平台编译问题,也想避免后备卫士的额外工作:
#pragma once #ifndef HEADER_H #define HEADER_H ... #endif // HEADER_H
我应该担心吗? 我应该在这方面花费更多精力吗?
#pragma once
使用#pragma once
应该可以在任何现代编译器上工作,但是我没有看到任何理由不使用标准的#ifndef
include guard。 它工作得很好。 有一点需要注意的是,GCC在版本3.4之前不支持#pragma once
。
我还发现,至less在GCC上, 它认识到标准的#ifndef
包括guard并优化了它 ,所以它不应该比#pragma once
慢#pragma once
。
#pragma once
有一个缺点(除非是非标准的),也就是说,如果你在不同的位置有相同的文件(我们有这个是因为我们的构build系统复制文件),那么编译器会认为这是不同的文件。
我希望#pragma once
(或类似的东西)已经在标准中。 包括警卫在内并不是什么大不了的事情(但是他们似乎有点难以向学习这门语言的人解释),但似乎是一个可以避免的轻微的烦恼。
事实上,自从99.98%的时间以来, #pragma once
行为就是期望的行为,如果防止多重包含头文件被编译器自动处理,使用#pragma
或者其他的东西来允许double包含,那将是非常好的。
但是我们有我们所拥有的(除了你可能没有#pragma once
)。
我不知道任何性能优势,但它当然有效。 我在我的所有C ++项目中使用它(授予我使用MS编译器)。 我觉得它比使用更有效
#ifndef HEADERNAME_H #define HEADERNAME_H ... #endif
它执行相同的工作,不用额外的macros填充预处理器。
GCC 从3.4版本开始正式支持#pragma once
。
GCC自3.4开始支持#pragma once
,请参阅http://en.wikipedia.org/wiki/Pragma_once以获得进一步的编译器支持。;
我看到使用#pragma once
,而不是包括守卫的好处是避免复制/粘贴错误。
让我们面对现实吧:我们大多数人几乎从头开始创build一个新的头文件,而只是复制一个现有的头文件并将其修改为我们的需要。 使用#pragma once
创build一个工作模板要简单得多,而不是包含防护。 我不得不修改模板越less,我可能会遇到错误。 同样的包括在不同文件中的警卫导致奇怪的编译器错误,并且需要一些时间来弄清楚什么地方出错了。
TL; DR: #pragma once
更容易使用。
使用' #pragma once
'可能没有任何作用(它在任何地方都不被支持 – 尽pipe它被越来越广泛地支持),所以你需要使用条件编译代码,在这种情况下,为什么要打扰' #pragma once
'呢? 无论如何编译器可能会优化它。 不过,这取决于你的目标平台。 如果你所有的目标都支持它,那么继续使用它,但是它应该是一个有意识的决定,因为如果你只使用编译指示,然后移植到不支持它的编译器上,那么所有的地狱都会崩溃。
我使用它,我很高兴,因为我不得不less打一个新的标题。 它在三个平台上工作得很好:Windows,Mac和Linux。
我没有任何性能信息,但是我相信#pragma和包含守卫之间的区别与parsingC ++语法的缓慢性没有任何关系。 这是真正的问题。 尝试使用C#编译器编译相同数量的文件和行,以查看差异。
最后,使用守卫或杂注,根本就不重要。
性能优势在于一旦读取了#pragma,就不必重新打开该文件。 有了守卫,编译器必须打开文件(这可能会花费很多时间),以获得不应该再次包含内容的信息。
这只是理论,因为一些编译器将自动不打开没有任何读取代码的文件,对于每个编译单元。
无论如何,所有的编译器都不是这样,所以理想的情况是#pragma一次不得不避免跨平台代码,因为它不是标准的/没有标准化的定义和效果。 但是,实际上,这比守卫要好得多。
最后,在这种情况下, 您可以确保从编译器获得最好的速度,而不必检查每个编译器的行为,这个更好的build议是同时使用pragma和guard。
#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替代一次,或者与标准的包含守护进行组合,我还没有看到加速。
我真的没有看到它的价值可能会混淆旧版本的gcc,甚至其他编译器,因为没有真正的节省。 我没有尝试所有的各种脱衣舞,但我敢打赌,它会混淆他们中的许多人。
我也希望早日得到通过,但是我可以看到“如果ifndef工作得很好,我们为什么需要这个说法?”。 考虑到C的许多黑暗的angular落和复杂性,包括守卫是最简单的,自我解释的事情之一。 如果你对预处理器的工作方式有一点了解,那么他们应该是自我解释的。
但是,如果您确实观察到显着加速,请更新您的问题。
今天的守旧派包括一个#pragma一样快。 即使编译器没有专门处理它们,当它看到#ifndef WHATEVER和WHATEVER时,它仍然会停止。 打开文件今天很便宜。 即使有改善,也会在毫秒级。
我只是不使用#pragma一次,因为它没有任何好处。 为了避免与其他包含警卫冲突,我使用类似于:CI_APP_MODULE_FILE_H – > CI =公司名称; APP =应用程序名称; 其余的是不言自明的。
主要的区别在于编译器必须打开头文件才能读取包含防护。 相比之下,编译指示一旦导致编译器跟踪该文件,并且在遇到同一文件的另一个包含时不执行任何文件IO。 虽然这听起来可以忽略不计,但可以很容易地扩大与大项目,特别是没有良好的标题包括学科。
也就是说,这些日子编译器(包括GCC)足够聪明,可以像对待编译指示一样对待包括守卫。 即他们不打开文件,并避免文件的IO惩罚。
在不支持编译指示的编译器中,我已经看到了一些麻烦的手动执行。
#ifdef FOO_H #include "foo.h" #endif
我个人喜欢#pragma一次,因为它避免了命名冲突和潜在的拼写错误的麻烦。 这也是比较优雅的代码。 这就是说,对于可移植的代码,除非编译器抱怨它,否则不应该伤害它们。
如果我们使用msvc或Qt(高达Qt 4.5),自GCC(高达3.4),msvc都支持#pragma once
,我可以看到没有理由不使用#pragma once
。
源文件名通常和平等的类名相同,而且我们知道,有时我们需要重构 ,重命名类名,那么我们不得不更改#include XXXX
,所以我认为手工维护#include xxxxx
并不是一件聪明的工作。 即使使用Visual Assist X扩展,维护“xxxx”也不是必需的工作。
给人们的补充说明,认为总是希望自动一次性包含头文件:数十年来,我使用双头或多头包含头文件来构build代码生成器。 特别是对于协议库存根的生成,我发现拥有一个非常便携且function强大的代码生成器,不需要额外的工具和语言,就非常舒服。 我不是唯一使用这个scheme的开发者,因为这个博客是X-Macros的展示。 如果没有缺失的自动防护,这是不可能的。