我已经阅读了有关此主题的所有内容,包括本网站上的一些非常有用的讨论,NASA编码指南和Google C ++指南。我甚至买了这里推荐的“物理C ++设计”书(对不起,忘记了名字)并从中得到了一些有用的想法。大多数消息来源似乎都同意 - 头文件应该是自包含的,即它们包含他们需要的内容,以便cpp文件可以包含头而不包括任何其他文件并且它将编译。我也明白了向前宣布而不是尽可能包括在内。
也就是说,如果foo.cpp
包含bar.h
和qux.h
,那么bar.h
本身包含qux.h
会怎样? foo.cpp
应该避免包含qux.h
吗?亲:清理foo.cpp
(减少“噪音”)。 Con:如果有人将bar.h更改为不再包含qux.h
,则foo.cpp
神秘地开始无法编译。还会导致foo.cpp
和qux.h
之间的依赖关系不明显。
如果您的答案是“cpp文件应该#include它需要的每个标头”,那么就会得出其逻辑结论,这意味着几乎每个cpp文件都必须#include <string>, <cstddef>
等等,因为大多数代码最终都会使用那些,如果你不应该依赖包括它们在内的其他标题,你的cpp需要明确地包含它们。这似乎是cpp文件中的很多“噪音”。
思想?
之前的讨论:
What are some techniques for limiting compilation dependencies in C++ projects?
Your preferred C/C++ header policy for big projects?
How do I automate finding unused #include directives?
ETA:受到此前讨论的启发,我编写了一个Perl脚本来连续注释掉每个'include'和'using',然后尝试重新编译源文件,找出不需要的东西。我还想出了如何将它与VS 2005集成,因此您可以双击以转到“未使用”包含。如果有人想要它让我知道......现在非常实验。
答案 0 :(得分:8)
如果您的答案是“cpp文件应该
#include
它需要的每个标头”,那么就会得出其逻辑结论,这意味着几乎每个cpp文件都必须#include <string>, <cstddef>
等等,因为大多数代码都会最终使用它们,如果你不应该依赖包括它们在内的其他标题,你的cpp需要明确地包含它们。
烨。这就是我喜欢的方式。
如果“噪音”太多无法承受,那么拥有一个“全局”包含文件就可以了,该文件包含通常的常用包含(如许多Windows程序中的stdafx.h
),并且包括在每个.cpp
文件的开头(也有助于预编译头文件)。
答案 1 :(得分:2)
我认为您仍应包含这两个文件。这有助于维护代码。
// Foo.cpp
#include <Library1>
#include <Library2>
我可以阅读并轻松查看它使用的库。如果Library2
使用Library1
,则会将其转换为:
// Foo.cpp
#include <Library2>
但我仍然看到Library1
代码,我可能会有些困惑。不难猜测其他一些库必须包含它,但它仍然是一个必须发生的思考过程。
明确意味着我不必猜测,即使是额外的微秒编译。
答案 2 :(得分:2)
我认为你应该包括两者,直到它变得无法忍受。然后重构你的类和源文件以减少耦合,理由是如果仅列出所有依赖项是繁重的,那么你可能有太多的依赖...
折衷可能是说,如果bar.h
中的某些内容包含一个类定义(或函数声明),它必然需要qux.h
中的另一个类定义,那么可以假定bar.h
/需要包含qux.h
,bar.h
中API的用户无需费心包括两者。
因此,假设客户想要将自己视为“真的”只对条形码API感兴趣,但也必须使用qux,因为它调用了一个按值获取或返回qux对象的bar函数。然后它可以忘记qux有自己的标题,并想象它是bar.h
中定义的一个大条形API,qux只是该API的一部分。
这意味着您不能指望,例如,搜索cpp文件以查找qux.h
以查找qux的所有客户端。但是我无论如何都不会依赖它,因为一般来说,很容易意外地错过显然包括已经间接包含的依赖,并且从未意识到你没有列出所有相关的标题。所以你可能不应该假设标题列表是完整的,而是使用doxygen(或gcc -M,或其他)来获取完整的依赖列表,并搜索它。
答案 3 :(得分:1)
如果foo.cpp包含bar.h和qux.h怎么样,但事实证明bar.h本身包含qux.h? foo.cpp应该避免包括qux.h吗?
如果foo.cpp
直接使用qux.h
中的任何内容,那么它应该包含此标头本身。否则,由于bar.h
需要qux.h
,我会依赖bar.h
包含所需的一切。
答案 4 :(得分:0)
每个头文件提供使用某种类型的服务所需的定义(类定义,某些宏,等等)。如果您直接使用通过头文件公开的服务,请包含该头文件,即使另一个头文件也包含它。另一方面,如果您仅间接使用这些服务,仅在其他头文件的服务的上下文中,请不要直接包含头文件。
在我看来,无论如何这都不是什么大问题。第二次包含头文件没有得到完全解析;除了第一次包含它之外,它基本上已经被注释掉了。随着事情的变化而你发现你使用的是一个你没有直接包含的头文件,添加它并不是什么大问题。
答案 5 :(得分:0)
考虑foo.cpp是否直接使用qux.h的一种方法是考虑如果foo.cpp不再需要包含bar.h会发生什么。如果foo.cpp仍然需要包含qux.h,那么应该明确列出它。如果它不再需要qux.h中的任何内容,则可能没有必要明确地包含它。
答案 6 :(得分:0)
我认为,“永不优化而不分析”规则适用于此。
(这个答案偏向“表现”而不是“混乱”;杂乱一般可以随意清理,不可否认,以后会变得更难,但性能会对你产生影响。)
尝试两种方式,看看您的编译器是否有任何显着的速度提升,然后才考虑删除“重复”标题 - 因为正如其他答案所指出的那样,通过删除重复项会产生长期可维护性惩罚
考虑使用类似Sysinternals FileMon的内容来查看您是否实际为这些重复包含生成文件系统命中(大多数编译器都没有,并使用正确的标头保护)。
我们在这里的经验表明,积极寻找完全未使用的标题(或者可能是正确的前向声明)并删除它们比计算包含链更值得花费时间和精力可能重复。一个好的棉绒(夹板,PC-Lint等)可以帮助你做出这个决定。
更值得花时间和精力的是弄清楚如何让我们的编译器在每次执行时处理多个编译单元(对于我们来说几乎是线性加速,直到某一点 - 编译完全由编译器启动控制)。
在那之后,你可能想要考虑“单一的大CPP”疯狂。它可以令人印象深刻。