考虑一个包含以下内容的
foo.cpp
文件
#include "foo.hpp"
int foo() {
return 7;
}
及其关联的标头
#pragma once
int foo();
显然需要后者来了解以下
main
函数是否存在 foo
:
#include <iostream>
#include "foo.hpp" // to make the name `foo` available
int main() {
std::cout << foo() << std::endl;
}
然而,
#include "foo.hpp"
似乎是多余的。我有什么理由应该保留它吗?
我在我工作的代码库中看到了这种做法,但我想开源中有很多可用的示例。例如,作为随机选取的示例,请查看
src/builtin_builtin.h
代码库中的 src/builtin_bultin.cpp
和 fish-shell
:前者在包含防护旁边,具有
只是
#include
,可以将 2 放入 fwd 标头中,将其与 1 一起包含在 cpp 文件中,然后 cpp 文件将不再需要包含自己的标头。
C++ 按线性顺序处理文件。如果您的 .cpp 文件调用同一 .cpp 文件中的其他方法,则需要使用定义来继续调用,否则您需要 .hpp 中的声明。对于两个相互递归的函数,您确实需要至少一个预先声明。
此外,拥有可用的类声明意味着编译器可以警告不匹配(由于重载,不适用于自由函数声明)
从其实现文件 (
fpp.hpp
) 中包含标头 (fpp.cpp
) 的一些原因:
如果您在
foo.hpp
中包含 foo.cpp
first,则
foo.cpp
的编译将充当 foo.hpp
是否为 self-contained 的测试,这意味着无需先包含任何其他内容即可成功编译。 Google C++ 编码指南特别建议首先包含关联的标头。
对于
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 几乎不会对编译时间产生可测量的差异。有有方法可以通过重组标头依赖性(例如,使用前向标头)来显着提高编译时间,但这不是其中之一。
致谢:国际收支平衡表在评论中指出了第 1 点。 MSalters 的回答 注意到第 3 点和第 4 点。