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

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


当前回答

我不认为它会对编译时间产生重大影响,但#pragma once在所有编译器中都得到了很好的支持,但实际上并不是标准的一部分。预处理器可能会快一点,因为它更容易理解你的确切意图。

#pragma一次性使用不太容易出错,需要输入的代码也更少。

为了加快编译时间,尽可能地向前声明而不是包含在.h文件中。

我更喜欢使用#pragma一次。

这篇维基百科文章介绍了两者同时使用的可能性。

其他回答

我不认为它会对编译时间产生重大影响,但#pragma once在所有编译器中都得到了很好的支持,但实际上并不是标准的一部分。预处理器可能会快一点,因为它更容易理解你的确切意图。

#pragma一次性使用不太容易出错,需要输入的代码也更少。

为了加快编译时间,尽可能地向前声明而不是包含在.h文件中。

我更喜欢使用#pragma一次。

这篇维基百科文章介绍了两者同时使用的可能性。

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

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

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

#pragma曾经有无法修复的错误。它不应该被使用。

如果你的#include搜索路径足够复杂,编译器可能无法区分两个具有相同基底名的头文件(例如a/foo.h和b/foo.h),因此在其中一个头文件中使用#pragma一次将同时屏蔽两个头文件。它也可能无法区分两个不同的相对include(例如#include "foo.h"和#include "../a/foo.h"指的是同一个文件,因此#pragma once将无法在应该包含冗余include的情况下抑制冗余include。

这也会影响编译器避免使用#ifndef守卫重读文件的能力,但这只是一种优化。使用#ifndef守卫,编译器可以安全地读取它不确定已经看过的任何文件;如果是错的,它只需要做一些额外的工作。只要没有两个头文件定义相同的保护宏,代码将按预期编译。如果两个头文件定义了相同的保护宏,程序员可以进去修改其中一个。

#pragma曾经没有这样的安全网——如果编译器对头文件的标识错误,无论哪种情况,程序都将编译失败。如果你遇到了这个错误,你唯一的选择是停止使用#pragma一次,或者重命名一个头文件。头文件的名称是API契约的一部分,因此重命名可能不是一个选项。

(简而言之,这是不可修复的,因为Unix和Windows文件系统API都没有提供任何机制来保证告诉你两个绝对路径名是否指向同一个文件。如果您认为inode编号可以用于此,那么对不起,您错了。)

(历史注:12年前,当我有权从GCC中删除#pragma和#import时,我没有这样做的唯一原因是苹果的系统头文件依赖于它们。现在回想起来,这不应该阻止我。)

(因为这个问题已经在评论中出现了两次:GCC开发人员确实花了很多精力让#pragma once尽可能可靠;参见GCC bug报告11569。然而,GCC当前版本中的实现仍然可能在合理的条件下失败,例如构建农场受到时钟倾斜的影响。我不知道其他编译器的实现是什么样的,但我不期望任何人做得更好。)

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

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

#pragma once
#ifndef _HEADER_H_
#define _HEADER_H_

...

#endif

在Konrad Kleine的解释之上。

简要总结:

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

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

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

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

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

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

即两者的结合。