C++头文件不包含其他头文件有什么好处?

6
我看到过这样的头文件风格,即头文件不包含其他头文件,相应的*.cpp文件必须包含所有依赖项(并且以正确的顺序包括它们)。在早期可能会使构建依赖跟踪更容易(但我只是猜测)。现在还有什么好的理由吗?
文件“B.h”:
#ifndef _B_h_
#define _B_h_

// Note we do not #include "A.h" that contains class A declaration.

class B
{
public:
   A a; // An A object.
};
#endif // _B_h_

"B.cpp" 文件:

#include "A.h" // Must include this before B.h, otherwise class A not defined in B.h
#include "B.h"

...

1
如果您希望将问题标题命名为某个名称,您可以编辑自己的问题标题。 - In silico
我怀疑你在现实生活中看到过那个例子,因为它无法编译。你必须能够访问每个成员对象的完整类型。 - Kerrek SB
2
@Seth:哦,明白了——是的,那似乎是可怕的做法 :-) 一般来说,每个实现文件“foo.cpp”都应该(能够)以#include "foo.h"开头。 - Kerrek SB
@Kerrek,是的,但我不明白那与这个有什么关系。 - Seth Carnegie
@Seth:如果你需要让b.cpp#include "b.h"开始,那么你就无法使用这种结构了。 - Kerrek SB
显示剩余2条评论
6个回答

8

是的,这是一个不好的做法,因为如果有人把顺序搞错了,他们会收到错误信息,他们可能无法弄清楚。如果所有头文件都有include guards,那么一个包含它所需所有其他头文件的头文件将不会成为问题,这就是应该的做法。


1
+1. 我觉得我在包含Windows的某个头文件时曾遇到过这种问题。文档上说类似“当你包含X时,也应该包含Y”。我的想法是:“嗯,如果X需要Y,那么为什么X自己不包含Y呢?” - MRAB
@MRAB 我也记得有类似的东西,但我不记得是哪个头文件了。 - Seth Carnegie
1
在包含<Windows.h>之前必须先包含<Winsock2.h>,否则<Windows.h>会包含不兼容(且已弃用?)的<Winsock.h> - André Caron

5
看起来编写该代码的人误解了减少包含头文件数量的常见建议。通常建议删除无关紧要的#include <>指令,以加快编译速度。确实,在大型项目中,这样做可以通过以下方式显著加速编译:
1. 减少编译任何给定源文件所需的头文件数量; 2. 减少在头文件更改后需要重新编译的源文件数量。
总的来说,人们会推荐(我参与的所有项目的编码标准都是如此)使用类的前向声明,除非定义在相关头文件中的类:
1. 用作基类; 2. 用作数据成员; 3. 具有不完整的官方规范(例如,标准库容器允许具有额外的模板参数,只要它们具有默认值,因此前向声明它们是非标准的)。
在情况1和2下,#include <>指令必须出现在所有依赖的源文件和头文件中的类定义之前。基本上,它只是将#include <>指令从头文件移动到每个依赖项中。它会产生更多的代码,并且没有任何好处(例如编译时间等将保持不变)。因此,这个建议也伴随着编码标准中的另一个条目:每个头文件应该可以“独立”编译(例如在源文件的第一行包含)。

4

这是一种非常糟糕的做法。让编译器自己确定正确的顺序,这样更不容易出错。头文件应该像它们自己被包含一样编译-也就是说,如果你有一个只包含#include "B.h"的TU。


编译器无法确定正确的顺序。甚至不应该有正确的顺序。 - Seth Carnegie
1
更重要的是,由于你绝对不可能在不包含A.h的情况下使用B.h,因此将A.h包含在B.h中是合乎逻辑的。你应该能够说,“我想要类B”,并且在任何情况下仅通过包含B.h就能实现这一点。 - Kerrek SB

2
现在是否有充分的理由这样做呢? 实际上没有。人们会做各种“有效”的事情,因为他们不知道更好的方法、不关心或者有更重要的事情需要担心。当你的语言依赖于预处理器而不是真正的模块导入系统时,就会发生这种情况。 我认为最佳实践是确保使用您的内容的人永远不必担心包含的顺序或隐藏的先决条件。当然,除非你被某些第三方愚蠢的宏技巧所迫。

2

是的,我建议在头文件中#including任何INTERFACE的依赖项。

在这种情况下,是的:我会#include“A”(因为B的接口依赖于A)。

否则,如果实现使用了“A”(但标头没有),我只会在.cpp中#include A(因为它不是接口的一部分)。

在任何情况下,我都不希望头文件的顺序有所影响,如果可能的话,通常头文件的顺序不应该重要。

在我看来……

附言: 尽管Bjarne Stroustrup希望不这样,预处理器和预处理器宏仍然存在。 在C语言领域,以及几乎所有Microsoft API中都如此。 尊重这个事实是一种好习惯。


2
我认为这是一种不好的风格,会导致难以理解的错误。
你可能这样做的原因是为了避免每次更改单个头文件时重新编译大量代码。但如果你发现自己处于这种情况,那么你可能有一个设计问题。

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