使用标题而不是重复

Posted

技术标签:

【中文标题】使用标题而不是重复【英文标题】:Using a header of headers instead of repeating 【发布时间】:2015-08-06 08:24:32 【问题描述】:

有什么理由不写一个#includes所有其他头文件的头文件,只在每个c文件中#include那个头文件?

设置每个头文件#ifndef ... #define ... #endif

澄清重复;我问的是用户编写的代码而不是系统标头,而其他可能的重复项没有说明为什么这不是一个好主意的答案。

感谢您的回答,我没有仔细考虑其中的一些问题。我将考虑采用一些较小的标头的混合方法来组合在一起的模块。 这是我接管的一个代码库,这是一个我正在慢慢尝试解开的网络。

【问题讨论】:

Is it right to simply include all header files?的可能重复 我不认为这是重复的@Jongware,他们不会问同样的事情! This 可能更适合复制。 【参考方案1】:

我们首先将头文件和实现文件分成单独的文件(除了代码可读性)的原因是为了让编译器能够进行增量构建。也就是说,如果我们在HeadearA.h 中进行更改,只有ImplA.cImplB.c 需要,则无需重新编译ImplC.cImplZ.c。但是,如果您按照自己的方式进行操作,编译器将无法知道这一点,并且您将在每次重建时重新编译所有 .c 文件,无论是否必要。对于构建时间较短的小型项目,这是可以的。对于构建时间接近 30 分钟的大型项目,这变得令人望而却步。

【讨论】:

30 分钟的构建时间...听起来像是一个 c++ 项目。 是的;公平地说,我见过的 C 项目的最长时间大约是 5 分钟左右,但我觉得原理是一样的。 如此大型的 C 项目相当少见。我可以想到 glibc、gcc、X-Windows、Linux 内核……大多数 C 项目在具有足够 RAM 的现代机器上几秒钟内从头开始重建。 @chqrlie:嗯...不知何故,我从事的大多数 (C) 项目通常在大约 2-3 分钟内完成,因为我通常从事较大的项目(等待仍然很烦人)。我想对于大多数项目来说,它应该没那么重要。 @chqrlie:取决于。我当前的项目包括相当多的自动生成的代码。但是 - 承认,它仍然可以在一分钟内从我的硬盘上的旧 core 2 duo 笔记本上编译。嗯.. 现在我很好奇在我的桌面上需要多长时间。【参考方案2】:

对于一个不平凡的项目,经常使用两种策略的组合:一个标头包括常用的系统标头,如stdbool.hstdint.hstdatomic.h 等。由于这些永远不会改变,因此请注意与重建相关。

您还可能包含大多数/所有模块所需的一些项目设置,因此您不必在编写新模块时关心它们(或在重构它们时更改每个模块)。

然后你有代表你的模块依赖树的特定标题。这些根据需要包括在内。这不仅加快了构建(而不是编译)项目的速度,而且还可以被doxygen 之类的文档工具用来自动为文档创建依赖关系树,因此您不必手动跟踪每个更改。 IDE 可以使用类似的方法来快速访问依赖的头文件/模块。

特别适用于 C 的一个缺点称为命名空间污染。由于 C 不允许自定义命名空间,因此每个包含的标头都会将其符号添加到当前编译单元的命名空间中。对于宏,这可能更糟,因为它们是纯文本替换,对上下文的了解非常有限(为此,还请检查 inline 与 函数宏)。

【讨论】:

“名称空间”污染和延长的构建时间是不将所有头文件组合到单个头文件中的两个主要原因。 makefile 等背后的整个想法是减少构建时间。我来自构建可能需要数小时、数小时和数小时的时代。在那些日子里(当程序小得多的时候),我们会尽我们所能来最小化文件编译的数量。现在,程序要大得多,编译文件通常几乎立即发生,但是当有成百上千个文件时,我们仍然需要尽我们所能来最小化 @user3629249:你真的看过我的回答吗?我只是详细的比。您当然不会认为系统标头会经常更改,不是吗?对于某些项目,构建时间与体面的主机硬件几乎无关,因此组合一些只是偶尔更改的标头也没有问题;特别是如果这些都包含在大多数模块中。那么它实际上没有任何区别。我想我清楚地说明了何时以及何时不使用哪种方法。我从事这项业务的时间足够长,也知道当时的情况。我当然也不反对 for 捆绑 all 标头。 什么答案?我刚刚又读了一遍整件事,我从你那里看到的只是两个 cmets,没有答案。 @user3629249:那我帮不了你——对不起。显然其他人的想法不同。【参考方案3】:

主要问题是你最终得到一大碗意大利面条代码,而且一切都知道......最终。你所做的任何改变都会产生影响,你最终不得不重构以重新模块化...... 你没有一点点提醒你实际上包含一些标题来做出 x 应该知道 y 的决定。

但除了符号可见性和宏扩展的正常问题(应该是边缘情况)之外,它并没有任何本质上的错误。

【讨论】:

【参考方案4】:

您的建议是可能的:许多项目都使用这种约定。

一些优点:

文件标题的简洁性 Makefile 中的依赖关系更简单,无需复杂的自动生成依赖关系列表 名称冲突的早期检测。 没有递归包含难题 没有因包含顺序不同而导致的细微错误 如果使用预编译头文件,编译速度会更快。

一些缺点:

对头文件的任何修改都会导致项目中的所有源文件都被重新编译。 鼓励缺乏适当的封装 单个文件的编译时间变慢(如果它成为性能问题,请使用预编译的标头修复此问题)

【讨论】:

以上是关于使用标题而不是重复的主要内容,如果未能解决你的问题,请参考以下文章

何时使用 Malloc 而不是 New [重复]

使用明确编号的重复而不是问号、星号和加号

使用 StringBuilder 而不是 StringBuffer 有啥特别之处 [重复]

使用 var 运算符而不是类类型 [重复]

为啥我使用@classmethod 而不是普通的实例方法[重复]

何时在 mysql 中使用 TEXT 而不是 VARCHAR [重复]