我正在开发一个已知只能在windows上运行并在Visual Studio下编译的代码库(它与excel紧密集成,所以它不会消失)。我想知道我是否应该使用传统的包含守卫或使用#pragma一次为我们的代码。我认为让编译器处理一次#pragma会产生更快的编译,并且在复制和粘贴时更不容易出错。它也稍微不那么丑陋;)

注意:为了获得更快的编译时间,我们可以使用冗余包含守卫,但这增加了包含文件和包含文件之间的紧密耦合。通常这是可以的,因为守卫应该基于文件名,并且只在你需要改变包含名称时才会改变。


当前回答

我回答了一个相关的问题:

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

我把答案也加在这里,以防有人被这个问题绊倒,而不是其他问题。

其他回答

#pragma一次允许编译器在再次出现该文件时完全跳过该文件——而不是解析该文件,直到它到达#include守卫。

因此,语义略有不同,但如果它们按照预期的方式使用,则它们是相同的。

两者结合可能是最安全的方法,因为在最坏的情况下(编译器将未知的pragma标记为实际错误,而不仅仅是警告),你只需要删除#pragma本身。

当你限制你的平台,比如说“桌面上的主流编译器”,你可以安全地省略#include守卫,但我在这方面也感到不安。

OT:如果你有其他关于加速构建的技巧或经验可以分享,我很好奇。

如果你确信你永远不会在不支持它的编译器中使用这段代码(Windows/VS, GCC和Clang是支持它的编译器的例子),那么你当然可以使用#pragma一次而不用担心。

您也可以两者都使用(参见下面的示例),这样就可以在兼容系统上获得可移植性和编译加速

#pragma once
#ifndef _HEADER_H_
#define _HEADER_H_

...

#endif

我通常不会使用#pragma,因为我的代码有时必须使用MSVC或GCC以外的东西进行编译(嵌入式系统的编译器并不总是有#pragma)。

所以我必须使用#include守卫。我也可以像一些回答建议的那样使用一次#pragma,但似乎没有太多的理由,而且它经常会在不支持它的编译器上引起不必要的警告。

我不确定这种务实会节省多少时间。我听说编译器通常已经识别出一个头文件中除了守卫宏之外的注释之外什么都没有,并且会在这种情况下执行#pragma。,不再处理该文件)。但我不确定这是真的还是编译器可以做这种优化。

在任何一种情况下,对我来说使用#include守卫更容易,它将在任何地方工作,而不用再担心它了。

我只是想补充一下这个讨论,我只是在VS和GCC上编译,并且习惯使用包含守卫。我现在已经切换到#pragma一次了,对我来说唯一的原因不是性能或可移植性或标准,因为我并不关心什么是标准,只要VS和GCC支持它,那就是:

#pragma一次性减少了出现错误的可能性。

将一个头文件复制并粘贴到另一个头文件,修改它以满足自己的需要,并且忘记更改include守卫的名称,这太容易了。一旦包含了两者,就需要花费一些时间来跟踪错误,因为错误消息并不一定是清晰的。

在Konrad Kleine的解释之上。

简要总结:

当我们使用# pragma一次时,编译器的责任很大,不允许它被包含多次。这意味着,在您在文件中提到代码片段之后,就不再是您的责任了。

现在,编译器在文件的开头寻找这个代码片段,并跳过它不被包含(如果已经包含一次)。这肯定会减少编译时间(在一般和大型系统中)。然而,在模拟/测试环境中,由于循环等依赖关系,将使测试用例的实现变得困难。

现在,当我们为头文件使用#ifndef XYZ_H时,维护头文件的依赖关系更多地是开发人员的责任。这意味着,每当由于一些新的头文件,有循环依赖的可能性,编译器只会在编译时标记一些“未定义的..”错误消息,由用户检查实体的逻辑连接/流程,并纠正不适当的包含。

这肯定会增加编译时间(因为需要纠正和重新运行)。此外,由于它是在包含文件的基础上工作的,基于“XYZ_H”定义状态,如果不能获得所有定义,仍然会报错。

因此,为了避免这种情况,我们应该使用,as;

#pragma once
#ifndef XYZ_H
#define XYZ_H
...
#endif

即两者的结合。