我已经读到有一些使用时的编译器优化#pragma once
可以导致更快的编译。我认识到这是非标准的,因此可能引起跨平台兼容性问题。
非Windows平台(gcc)上的大多数现代编译器都支持这种功能吗?
我想避免平台编译问题,但也想避免后备防护的额外工作:
#pragma once
#ifndef HEADER_H
#define HEADER_H
...
#endif // HEADER_H
我应该担心吗?我是否应该为此花费更多的精力?
我已经读到有一些使用时的编译器优化#pragma once
可以导致更快的编译。我认识到这是非标准的,因此可能引起跨平台兼容性问题。
非Windows平台(gcc)上的大多数现代编译器都支持这种功能吗?
我想避免平台编译问题,但也想避免后备防护的额外工作:
#pragma once
#ifndef HEADER_H
#define HEADER_H
...
#endif // HEADER_H
我应该担心吗?我是否应该为此花费更多的精力?
Answers:
使用#pragma once
应该适用于任何现代编译器,但是我看不出没有使用标准#ifndef
包含保护的任何理由。它工作正常。一个警告是,GCC #pragma once
在3.4版之前不支持。
我还发现,至少在GCC上,它可以识别#ifndef
包含保护的标准并对其进行优化,因此它的速度不应慢于#pragma once
。
#pragma once
通常更快,因为文件没有被预处理。ifndef/define/endif
无论如何都需要进行预处理,因为在此块之后,您可以得到一些可编译的文件(理论上而言)
#ifndef FOO_BAR_H
通常还必须为“ foo_bar.h”之类的文件定义一个诸如的新符号。如果以后重命名此文件,是否应该相应地调整包含保护以符合此约定?另外,如果在代码树的两个不同位置有两个不同的foo_bar.h,则必须为每个符号考虑两个不同的符号。简短的答案是使用#pragma once
,如果您确实需要在不支持它的环境中进行编译,请继续为该环境添加包含保护。
#pragma once
确实有一个缺点(不是非标准的),也就是说,如果您在不同的位置有相同的文件(我们有这个文件是因为我们的构建系统会在周围复制文件),那么编译器会认为这些是不同的文件。
#ifdef
宏值也可以不同。
我希望#pragma once
(或类似的东西)已经成为标准。包括后卫并不是什么大问题(但似乎很难向学习该语言的人解释),但是似乎可以避免一些小麻烦。
实际上,由于在99.98%的时间内,此#pragma once
行为是所需的行为,如果防止编译器自动处理标头的多次包含,并带有#pragma
或允许双重包含的内容,那将是很好的选择。
但是我们拥有我们所拥有的(除了您可能没有拥有#pragma once
)。
#import
指令。
GCC #pragma once
自3.4开始支持,请参见http://en.wikipedia.org/wiki/Pragma_once以获取进一步的编译器支持。
我认为,#pragma once
与包括防护措施相比,最大的好处是避免复制/粘贴错误。
面对现实:我们大多数人几乎都不会从头开始创建新的头文件,而只是复制现有的头文件并根据需要对其进行修改。使用#pragma once
而不是包含防护来创建工作模板要容易得多。我修改模板的次数越少,发生错误的可能性就越小。在不同的文件中使用相同的include防护会导致奇怪的编译器错误,并且需要花费一些时间来找出问题所在。
TL; DR:#pragma once
更易于使用。
我使用它并对此感到满意,因为我不得不键入更少的内容来制作新的标题。对于我来说,它在以下三个平台上运行良好:Windows,Mac和Linux。
我没有任何性能信息,但我相信与分析C ++语法的速度较慢相比,#pragma和include Guard之间的区别将是什么。那是真正的问题。例如,尝试使用C#编译器编译相同数量的文件和行,以了解两者之间的区别。
最后,使用警卫或实用程序根本没有关系。
使用' #pragma once
'可能没有任何效果(尽管越来越广泛地支持它,但并不是所有地方都支持它),因此无论如何都需要使用条件编译代码,在这种情况下,为什么要打扰' #pragma once
'?编译器可能仍会对其进行优化。但是,它确实取决于您的目标平台。如果您的所有目标都支持它,那么请继续使用它-但是这应该是一个明智的决定,因为如果仅使用编译指示,然后移植到不支持该编译器的编译器,那么一切都会变得一团糟。
性能优势在于,一旦读取了#pragma,就不必重新打开文件。使用警卫,编译器必须打开文件(这可能会花费很多时间),以获取不应再次包含其内容的信息。
这只是理论上的原因,因为对于每个编译单元,某些编译器将不会自动打开没有任何读取代码的文件。
无论如何,并非所有编译器都如此,因此理想情况下,一次跨平台代码必须避免使用#pragma,因为它根本不是标准的,也没有标准化的定义和效果。但是,实际上,这确实比后卫更好。
最后,更好的建议是确保同时使用编译指示和防护,而不必在这种情况下检查每个编译器的行为,从而确保从编译器获得最佳速度。
#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once
#include "Thing.h"
namespace MyApp
{
// ...
}
#endif
这样一来,您可以同时兼顾两者(跨平台和帮助编译的速度)。
由于键入时间越来越长,我个人使用了一种工具来帮助您以非常灵巧的方式生成所有内容(Visual Assist X)。
pragma
之后ifndef
?有好处吗?
不总是。
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566给出了一个很好的示例,说明两个文件都应包括在内,但由于时间戳和内容相同(文件名不同)而被误认为是相同的。
#pragma once
这是非标准的,因此无论编译器决定做什么,都是“正确的”。当然,我们可以开始讨论什么是“期望的”和什么是“有用的”。
在非常大的树上使用gcc 3.4和4.1(有时使用distcc),当使用#pragma代替或与标准include防护结合使用时,我还没有看到任何加速。
我真的看不到它的价值如何可能会混淆旧版本的gcc,甚至其他编译器,因为没有真正的节省。我没有尝试过所有各种各样的问题,但是我敢打赌它会使许多问题混淆。
我也希望它能早日被采用,但是我可以看到这样的论点:“当ifndef运行得很好时,为什么我们需要它?”。考虑到C的许多黑暗角落和复杂性,包括后卫是最容易自我解释的事情之一。如果您对预处理器的工作原理了解甚少,那么它们应该可以自我解释。
但是,如果您确实观察到明显加快了速度,请更新您的问题。
如今,守旧派的守卫者像#pragma一样快。即使编译器未对它们进行特殊处理,当看到#ifndef WHATEVER和WHATEVER定义后,它仍将停止。今天打开文件是很便宜的。即使有改进,也要几毫秒的时间。
我只是不使用#pragma,因为它没有好处。为了避免与其他包含防护冲突,我使用类似以下内容的命令:CI_APP_MODULE_FILE_H-> CI =公司缩写;APP =应用名称;其余的不言而喻。
主要区别在于,编译器必须打开头文件才能读取包含保护。相比之下,编译指示曾经使编译器跟踪该文件,并且在遇到同一文件的另一个包含时不执行任何文件IO。虽然这听起来可以忽略不计,但它可以轻松地随大型项目扩展,尤其是那些没有良好标题的项目。
也就是说,如今的编译器(包括GCC)足够聪明,可以一次将杂乱无章的内容包括在内。即他们不打开文件,避免文件IO损失。
在不支持编译指示的编译器中,我看到了一些麻烦的手动实现。
#ifdef FOO_H
#include "foo.h"
#endif
我个人喜欢#pragma一旦方法,因为它避免了命名冲突和潜在的拼写错误的麻烦。相比之下,它也是更优雅的代码。就是说,对于可移植代码,除非编译器对此有所抱怨,否则两者都不会受到损害。
#pragma once
现在还更长!
给人们以为始终需要自动一次性包含头文件的人们的附加说明:几十年来,我使用头文件的双重或多重包含来构建代码生成器。特别是对于生成协议库存根,我发现拥有一个极其可移植且功能强大的代码生成器而无需其他工具和语言,这让我感到非常舒适。X-Macros博客显示,我不是唯一使用此方案的开发人员。没有缺少的自动防护,这是不可能的。
X-Macro
功能中看到您的观点,但这不是include的主要用途,如果我们坚持使用DRY,不是应该像header unguard /#pragma multi这样吗?
#pragma once
似乎避免了VS 2008中的某些类视图问题。#pragma once
基于这个原因,我正在摆脱包含保护并将它们全部替换的过程。