仅在接口中包含标头,而不在实现中包含标头

Only include headers in interface, not in implementation

本文关键字:包含标 实现 接口      更新时间:2023-10-16

我的头文件看起来像:

// A.hpp --- A's interface
#ifndef MY_H
#define MY_H
#include<string>
#include<vector>
class A {
 public:
  A(const std::string& name);
  std::vector<double> foo() const;
 private:
  std::string m_name;
};
#endif

我的实现文件如下所示:

// A.cpp --- A's interface implementation
#include<A.hpp>
#include<implementation_detail.hpp>
A::A(const std::string& name)
 : m_name(name) { }
std::vector<double> A::foo() const {
 std::vector<double> r;
 r.push_back(1);
 return r;
}

我的立场是,我不#include stringvector实现文件中,因为它们已经在接口中声明,并且在实现文件中#include它们充其量是多余的,在更糟糕的情况下是有害的**。

当然,A 的实现将#include界面上看不到的所有实现细节

的问题:我说得对吗?这种做法会对我的代码产生负面影响吗?

** 这是牵强附会的,但是包含的数量(以及使用的包含保护)可以影响非常大的项目的编译速度;这篇文章很有趣。

这是风格和个人喜好的问题。

对于头文件,

我个人的偏好是使头文件独立存在,但只是勉强。通过"独立",我的意思是我应该能够在某个随机源文件中#include该标头,并且该源文件仍将编译。

#include "some_random_header_file.h"
int main () {}

以上应始终编译。通过"勉强",我的意思是标头应该没有无端的#include指令。如果某些#include不提供直接在头文件中使用的功能,则我不会在头文件中#include该其他头文件。

对于源文件,我个人的偏好是#include提供源文件中使用的功能的每个标头。如果源文件调用的代码std::string::append,则该源文件具有更好的#include <string> - 即使其他标头已经包含它。

你是对的。cpp 文件是编译单元。这就是使用标头的地方。这些标头包含的所有内容也有效地存在于 cpp 文件中。

值得注意的是:

  • 不要在标题的 cpp 文件中包含您需要的内容。
  • 标头中的类型中明确。 即将std::string用作类型不using std::string甚至更糟using namespace std;在标题中。
  • 可以在 cpp 文件中方便地使用 using 语句。

您不希望向包括标题在内的人员引入不必要的类型。

这是风格问题,而不是正确性。

但是,包含绝对需要的东西被认为是一种很好的风格。因此,如果您的接口需要这些标头,那么您的.h必须包含它们。

同时,使每个标头自给自足被认为是一种很好的样式 - 也就是说,为了使用其设施,包含它而不包含任何其他内容就足够了。

因此,鉴于这两点,您在标头中包含这些#include指令是正确的。

如果您真的关心#include性能,这里有一些选择:

  • 前瞻声明
  • 痘痘成语
  • 预编译标头