考虑具有以下内容的foo.cpp
文件
#include "foo.hpp"
int foo() {
return 7;
}
及其相关联的报头
#pragma once
int foo();
后者显然需要意识到foo
存在的以下main
函数:
#include <iostream>
#include "foo.hpp" // to make the name `foo` available
int main() {
std::cout << foo() << std::endl;
}
然而,#include "foo.hpp"
似乎是多余的。我有什么理由保留它吗?
我在我工作的代码库中看到过这种做法,但我想开源中有很多例子。例如,作为一个随机选取的示例,从fish-shell
代码库中查看src/builtin_builtin.h
和src/builtin_bultin.cpp
:前者在include保护之外,具有仅
- 一个CCD_ 8
- 两个类声明
- 以及函数声明
可以将2放在fwd头中,将其与1一起包含在cpp文件中,然后cpp文件就不需要再包含自己的头了。
C++以线性顺序处理文件。如果.cpp文件从同一.cpp文件调用其他方法,则定义需要继续调用,否则您需要.hpp中的声明。对于两个相互递归的函数,您确实需要至少一个声明。
此外,具有可用的类声明意味着编译器可以警告不匹配(由于重载,无法使用自由函数声明(
从其实现文件(fpp.cpp
(中包含标头(fpp.hpp
(的一些原因:
-
如果在
foo.cpp
中首先包含foo.hpp
,那么foo.cpp
的编译就相当于测试foo.hpp
是自包含的,这意味着它可以在不首先包含任何其他内容的情况下成功编译。GoogleC++编码指南特别建议首先包含相关的头。 -
foo.hpp
最好声明foo.cpp
导出的所有内容,而foo.cpp
中的所有其他内容都具有内部链接,以免污染全局命名空间。GCC选项-Wmissing-declarations
将报告与该实践的偏差,但仅当foo.cpp
包括foo.hpp
时才有效。 -
有时(事实上,经常(需要包含
foo.hpp
,因此foo.cpp
将进行编译。最常见的情况是,foo.hpp
定义了foo.cpp
需要的类型,但也可能由于在定义之前使用而需要函数声明。我建议坚持这样做,而不是试图推断在每种情况下是否有必要。 -
有时,编译器可以诊断声明和定义之间的不匹配。我看到的一个讨厌的例子是,一个头声明了全局
int x
,但实现文件定义了long x
。花一天的时间调试这个错误,我预测你会下定决心每次都包含相关的头!
最后,没有充分的理由不包含相关的标头。特别是,省略include几乎永远不会对编译时间产生可衡量的影响。有方法可以通过重组标头依赖关系(例如,使用前向标头(来显著提高编译时间,但这不是其中之一。
鸣谢:BoP在评论中提到了第1点。MSchangers的答案注释第3点和第4点。