【问题标题】:Why place headers in a separate directory? [duplicate]为什么将标题放在单独的目录中? [复制]
【发布时间】:2012-12-20 07:36:20
【问题描述】:

我知道在 C/C++ 项目中将头文件放在诸如include 之类的目录中并将实现放在诸如src 之类的单独目录中是很常见的。我一直在玩弄不同的项目结构,想知道这是否有任何客观原因,还是只是惯例?

【问题讨论】:

标签: c++ c directory-structure


【解决方案1】:

约定是原因之一 - 大多数时候,通过有效的抽象,您只关心接口并希望只看标题就可以轻松。

但这并不是唯一的原因。如果你的项目是按模块组织的,你很可能必须在不同的模块中包含一些头文件,并且你希望你的包含目录中的其他“噪音”文件被清除。

此外,如果您计划重新分发您的模块,您可能希望隐藏实现细节。因此,您只需提供标头和二进制文件 - 从一个文件夹中分发标头,其中没有其他内容更简单。

还有一个我实际上更喜欢的替代 - 公共标头放在单独的文件夹中(这些包含最小接口 - 没有任何实现细节可见),私有标头和实现文件是分开的(可能但不一定在单独的文件夹中)。

【讨论】:

    【解决方案2】:

    我更喜欢将它们放在 same 目录中。原因:

    接口规范文件和实现该接口的源文件属于项目的同一部分。假设你有subsystemx。那么,如果你把subsystemx文件放到subsystemx目录下,subsustemx是自包含的。

    如果有很多包含文件,当然可以使用subsystemx/include 和subsystemx/source,但是我认为如果你将class Foo 的定义放在foo.hpp 和foo.cpp 中,你肯定想看到他们两个(或者至少有可能很容易地做到这一点)在一个目录列表中。查找与foo相关的所有文件

    ls foo*
    

    查找所有实现文件:

    ls *.cpp
    

    查找所有声明文件:

    ls *.hpp
    

    简单干净。

    【讨论】:

    • 但是subsystemx 之外的代码需要使用公共接口——因此公共接口标头需要易于访问并受到控制。如果您必须猜测子系统x 的哪些标头仅供内部使用,哪些标头用于外部使用——哪些定义了 API,您能想象一下混乱吗?私有标头可以与源代码一起使用没有问题。但是消费者需要的公共标头——需要以更可控的方式提供。
    • @JonathanLeffler 我设计了所以没有私有标题。 #include "subsystemx/foo.hpp" 总是有意义的。如果有内部细节,我将它们隐藏在实现文件中。另外,您如何确定标头仅供私人使用?
    • 你很幸运能在这么小的系统上工作。
    • @JonathanLeffler 另外,如果你需要私有头文件,添加一个后缀_internal 可以添加到文件名中。
    • 我同意这种做法。有一些工具(例如 autotools)可以分发您的项目,安装 make 目标会确定要安装到 PREFIX 中的内容。
    【解决方案3】:

    它使您的文件夹结构更清晰。头文件和源文件明显不同,用于不同的事物,因此将它们分开是有意义的。从这个角度来看,问题基本上与“为什么源文件和文档放在不同的文件夹中”相同?计算机对于您放入文件夹中的内容和未放入的内容是高度不可知的,由于我们人类解析、存储和调用信息的方式,文件夹在大多数情况下只是一个方便的抽象。

    还有一个事实是,头文件即使在您构建之后仍然有用,也就是说,如果您正在构建一个库并且有人想要使用该库,他们将需要头文件 - - 不是源文件 - 因此它可以将这些头文件捆绑起来 - 获取 bin 中的内容和 include 中的内容,而不必筛选 src 中的内容 - 更容易。

    【讨论】:

      【解决方案4】:

      除了(有争议的?)对保持事物有序、在其他项目中有用等有用之外,还有一个非常中立和客观的优势:编译时间。

      特别是在一个包含一大堆文件的大型项目中,这取决于头文件的搜索路径(.c/.cpp 文件使用#include "headername.h" 而不是#include "../../gfx/misc/something/headername.h" 并且编译器传递了正确的参数以便能够吞下它)你大大减少了需要由编译器扫描以寻找正确的标题的条目数量。由于大多数编译器为每个编译的文件单独启动,它们需要读取包含路径上的文件列表并为每个编译文件寻找正确的头文件。如果包含路径上有一堆 .c、.o 和其他不相关的文件,则在其中查找包含所需的时间相应地更长。

      【讨论】:

      • 为什么编译器会扫描.c文件来匹配头文件,它知道路径,它只将#include <path>附加到给定的包含目录并检查文件是否存在,其他文件不存在在这个过程中交互,所以我不明白你的意思?
      • 并检查文件是否存在 - 根据文件系统,检查文件是否存在于目录中的复杂度可能在 O(log(n)) 和 O(n ) (除了 O(n^2) 的一些可耻的,幸运的是罕见和过时的例外) - 无论如何,目录中的文件越多,找到特定文件的时间就越长,虽然每个文件可能需要几微秒,但它是数百万个这样的查询一个大项目的编译,它加起来。
      • 啊,谢谢你的澄清。
      【解决方案5】:

      简而言之,有几个原因:

      • 可维护的代码。
      • 代码设计精良且整洁。
      • 更快的编译时间(有时,为了完成较小的更改)。
      • 更轻松地隔离文档等接口。
      • 可以避免编译时的循环依赖。
      • 易于查看。

      请看文章Organizing Code Files in C and C++ 解释得很好。

      【讨论】:

      • 您能解释一下这种分离如何降低循环依赖的风险吗?
      • 我很确定 @parasrish 正在回答一个不同的问题“为什么将标头分成单独的 文件”,而不是像 OP 要求的那样将目录分开。
      猜你喜欢
      • 2023-03-22
      • 2011-07-07
      • 2014-06-21
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 2011-04-05
      • 1970-01-01
      • 2010-12-21
      相关资源
      最近更新 更多