【问题标题】:Why would I compile 2 C/++ source files instead of using a header file?为什么我要编译 2 个 C/++ 源文件而不是使用头文件?
【发布时间】:2020-10-24 15:16:25
【问题描述】:

我最近开始学习 makefile,我正在观看的视频包括两个源文件和一个头文件的编译。在头文件中定义了一个类,该类包含在两个源文件中。该类的方法在一个源文件中定义,并在第二个源文件(main.cpp)中调用。为什么我需要有 2 个源文件 (.c/.cpp)?

【问题讨论】:

  • 你明白为什么你应该把代码分成标题和源代码吗?一旦你这样做了,你也会知道为什么你不想把所有东西都放在标题中。
  • @idclev463035818 你能解释一下为什么我应该把标题和源分开吗?据我了解,这是出于组织目的。
  • 在预处理过程中将标头复制到源文件中。结果是一个输入编译器的庞大文件。如果一切都在标题中,那么每次您进行更改时,都会构建一个文件。它将包括项目的标头价值,并且一个更改会导致所有文件都被重新编译。这会非常耗时。
  • 阅读How does the compilation/linking process work?。当你完成后,剩下的应该是有意义的。如果您有一个标识符多次存在,链接器将不知道它应该使用哪个标识符。如果您有两个带有main 的文件,这两个文件都会编译,但链接器无法选择使用哪一个。
  • 将实际方法和函数实现放入头文件中,当目标文件链接在一起时,可能会存在相同方法或函数的多个实例。这可能会显着增加大型代码基线的编译时间,因为必须多次编译相同的函数或方法。如果使用这样的方案,如果函数或方法的多个实例可用,也可能会引发各种微妙的问题。

标签: c++ c makefile header-files


【解决方案1】:

人类的大脑一次无法容纳太多信息,因此我们将事物分割成更小的、合乎逻辑且连贯的片段。

好的。因此,一个 main.cpp 包含程序中的数十个或数百个或数千个文件,所有文件都在合理大小的头文件中实现,每个包含一个概念或聚合更多头文件,如果一个概念过于宽泛而难以描述1 在单个标题中。问题解决了,对吧?对。但这只是一个问题。

资源消耗呢?

在继续之前阅读How does the compilation/linking process work? 会很有帮助。

在预处理期间,每个包含语句都将替换为包含文件的内容。结果是一个输入编译器的庞大文件。这会占用大量内存。此外,如果一个文件包含所有内容作为标题,那么每次您进行更改时,无论多么小,都需要构建这个文件。它将包括项目的标头价值,并且一个更改会导致所有文件都被重新编译。这会非常耗时。

内存越来越便宜,因此限制第一个资源并不像 1970 年代发明所有这些时那样重要2。还时不时抬起它丑陋的脑袋。这就是为什么我在一台又大又胖的 PC 上交叉编译而不是直接在 Raspberry Pi 上构建代码的原因。

时间不会变得更便宜。从来没有,永远不会。

但是如果您遵循最佳实践并且标头包含一个接口(它的作用)而不是一个实现(它是如何实现的),您会发现标头并没有太大变化。大部分时间发生变化的是实现文件中的操作方法细节。一个小的更改仅限于提供该更改行为的一个实现文件。可能这是唯一需要重新编译的文件。这是从每次一个文件到每次一两个文件的巨大改进。

在极少数情况下界面发生变化,好吧,你把它搞砸了。

1 因为这就是代码:对行为的描述。它不是计算机要执行的指令列表——这是编译器的输出——它是对程序行为的描述。编译器的工作是将您的描述转化为指令。

2这也解释了为什么用最近创建的语言构建要简单得多3。在 1/2 K 内存和千赫频率的 CPU 的辉煌岁月里,他们没有留下任何包袱。他们也从很多错误中吸取了教训。

3 无论如何,对于最终用户。现代构建系统的后端是一些疯狂的代码,伙计。

【讨论】:

    【解决方案2】:

    每次您对头文件进行任何更改时,包含它的每个文件都必须重新编译,但如果您更改源文件该源文件需要重新编译。

    在处理非平凡规模的项目时,这可以显着减少编译时间。由于您的大部分工作通常是在实现方面完成的,因此对头文件的修改不如对源文件的频繁修改,因此大多数重新编译都将本地化。

    出于可导航性的原因,您还需要将源代码拆分为单独的文件。处理一个超过 2000 行的源文件比处理 10 个大约 200 行的文件要麻烦得多。当版本控制启动并且您在团队中工作时,这一点更为重要,因为这样可以减少合并冲突。

    想象一下,如果 Chrome 只是一个单一的 .cpp 文件。即使对它进行最微不足道的更改也需要重新编译整个东西,即使在装备精良的机器上也需要 6-12 个小时。相比之下,编译单个源文件并重新链接只需几分钟。

    在实践中,通常每个源文件都有一个类和相应的头文件。函数在逻辑上组合成集合,每个集合在它们自己的一对头文件/源文件中。

    【讨论】:

      猜你喜欢
      • 2015-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-30
      • 2015-12-15
      相关资源
      最近更新 更多