自围堵

现代标题应该是自包含的,这意味着需要使用 header.h 定义的工具的程序可以包含该标题(#include "header.h"),而不必担心是否需要首先包含其他标题。

建议:头文件应该是自包含的

历史规则

从历史上看,这是一个有点引起争议的话题。

一个又一个千年, AT&T Indian Hill C 风格和编码标准规定:

头文件不应嵌套。因此,头文件的序言应描述其他标头需要 #included 才能使标头正常工作。在极端情况下,如果要将大量头文件包含在多个不同的源文件中,则可以将所有常用的 #include 放在一个包含文件中。

这是自我遏制的对立面。

现代规则

然而,从那时起,意见倾向于相反的方向。如果源文件需要使用头文件 header.h 声明的工具,程序员应该能够写:

#include "header.h"

并且(仅限于在命令行上设置正确的搜索路径),header.h 将包含任何必要的先决条件标头,而无需向源文件添加任何其他标头。

这为源代码提供了更好的模块化。它还保护源免受在添加此标头后添加此标头的原因这一难题,这些难题在代码被修改并被黑客攻击了十年或两年后出现。

美国航空航天局戈达德太空飞行中心(GSFC)对于 C 编码标准是更现代的标准之一 -但现在是有点难以追查。它声明标题应该是自包含的。它还提供了一种确保标头自包含的简单方法:标头的实现文件应包含标头作为第一个标头。如果它不是自包含的,那么该代码将无法编译。

GSFC 给出的理由包括:

§2.1.1 标题包括基本原理

该标准要求单元的标题包含单元标题所需的所有其他标题的 #include 语句。首先在单元体中放置单元头的 #include 允许编译器验证头部是否包含所有必需的 #include 语句。

本标准不允许的替代设计允许标题中没有 #include 语句; 所有 #includes 都在 body 文件中完成。然后,单元头文件必须包含#ifdef 语句,以检查所需的标头是否包含在正确的顺序中。

备用设计的一个优点是正文文件中的 #include 列表正是 makefile 中所需的依赖列表,编译器会检查此列表。使用标准设计,必须使用工具来生成依赖关系列表。但是,所有分支推荐的开发环境都提供了这样的工具。

备用设计的主要缺点是,如果单元的所需标题列表发生更改,则必须编辑使用该单元的每个文件以更新 #include 语句列表。此外,编译器库单元所需的标头列表在不同的目标上可能不同。

备用设计的另一个缺点是必须修改编译器库头​​文件和其他第三方文件以添加所需的 #ifdef 语句。

因此,自我遏制意味着:

  • 如果头文件 header.h 需要一个新的嵌套头文件 extra.h,则不必检查每个使用 header.h 的源文件,看看是否需要添加 extra.h
  • 如果头文件 header.h 不再需要包含特定的头文件 notneeded.h,则不必检查每个使用 header.h 的源文件,看看是否可以安全地删除 notneeded.h(但请参阅包含你使用的内容)
  • 你不必为包含先决条件标头建立正确的顺序(这需要拓扑排序才能正确完成工作)。

检查自我控制

请参阅链接静态库以获取脚本 chkhdr,该脚本可用于测试头文件的幂等性和自包含性。