我应该仍然使用#include guards和#pragma once吗?

35

这里输入图像描述

http://en.wikipedia.org/wiki/Pragma_once
当所有编译器都支持#pragma once时,我是否仍然需要使用包含保护?
许多Stack Overflow上的回答都建议为了兼容性而同时使用两种方法,但我不确定这是否仍然正确。
现在有哪些编译器不支持#pragma once呢?

我不确定在广泛采用之前,同时使用两种方法只是一种推荐,还是说仍然存在非常好的理由来使用这两种方法。
只使用#pragma once会导致什么问题的例子有哪些?


11
请记住,列出的旧版本编译器可能不支持此功能,因此如果您正在开发一个要分发源代码的开源程序,则#pragma指令可能无法使用。 - Some programmer dude
5
这不仅涉及编译器的支持,还取决于环境的复杂性。你相信编译器可以肯定地知道两个文件是否相同,包括所有网络挂载和符号链接吗? - Bo Persson
2
还要考虑辅助工具,如索引器和分析器。它们可能没有完整的预处理器或解析器,就像编译器一样。 - justin
许多嵌入式系统使用旧的RVCT(或ADS)编译器。如果它们支持#pragma once,我会非常惊讶。 - Leo
1
@Leo RVCT至少从2.0版本开始支持#pragma once。 - Yaur
3个回答

14

这取决于您期望程序有多少可移植性。

只要您编写的程序应该与您确切知道支持 #pragma once 的编译器一起工作,那么仅使用 #pragma once 就足够了。但是这样做将限制您的程序为支持此实现定义特性的编译器集。

如果您需要程序在所有编译器上都能工作,则应同时使用 #pragma once 和 include guards。

如果编译器不支持 #pragma once,它将简单地忽略它[Ref#1],在这种情况下,头文件保护将为您服务,因此使用它们两个并没有错,当您不知道目标编译器支持的特性时。

因此,如果您希望程序在不同的编译器上具有100%的可移植性,理想的方式仍然是仅使用include guards。正如@CharlesBailey正确指出的那样,由于 #pragma once 的行为是实现定义的,因此未知编译器上的行为可能会对程序产生不利影响。


[Ref#1]
Standard C++03: 16.6 Pragma directive

形式为

# pragma pp-tokensopt new-line

的预处理指令会导致实现以实现定义的方式运行。任何未被实现识别的pragma都将被忽略。


7
如果你希望你的程序百分之百地可移植,你需要使用头文件保护机制,但同时需要避免使用#pragma once,因为在未知编译器上的实现定义行为可能对你的程序产生不利影响。 - CB Bailey
@CharlesBailey:同意。这个答案需要修改。我会去做的。感谢你指出。 - Alok Save
3
如果同一个文件在操作系统/文件系统层面被两个不同的名称别名,那么 #pragma once 会失败。但是,在文件中使用的 #include-guard 名称不受文件系统引用的影响,仍然能够正常工作。 - abelenky

10

这是一个非标准的写法,如果想保险起见,请使用包含防卫(include guards)。


7
根据您的表格显示,现在很少遇到不支持#pragma once的主流编译器。为了保持代码库的整洁和易于维护,需要不断进行重构。每次重命名类或移动一些代码时都必须更新包含保护,这给维护工作增加了很大的负担。

因此,除了某些特定情况或破损的构建系统外,实际上可以放心地依赖于#pragma once。如果您关心生产力和代码质量,只使用#pragma once似乎是显而易见的选择。

例外情况是,如果您正在编写需要支持所有编译器的库,或者不幸地使用了其中一个不具备此功能的罕见编译器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接