为什么我要编译 2 个 C/++ 源文件而不是使用头文件?
Why would I compile 2 C/++ source files instead of using a header file?
我最近开始学习 makefile,我正在观看的视频包括两个源文件和一个头文件的编译。在两个源文件中都包含的头文件中定义了一个 class。 class 的方法在一个源文件中定义,并在第二个源文件 (main.cpp) 中被调用。为什么我需要 2 个源文件 (.c/.cpp)?
每次您对头文件进行任何更改时,每个包含它的文件都必须重新编译,但是如果您更改源文件只有该源文件需要重新编译。
当处理一个非常大的项目时,这可以显着减少编译时间。由于您的大部分工作通常是在实现方面完成的,其中对头文件的修改不如对源文件的修改频繁,因此大多数重新编译将被本地化。
出于可导航性的原因,您还需要将源代码拆分成单独的文件。处理一个 2000 多行的源文件比处理 10 个文件(每个文件大约 200 行)要麻烦得多。当版本控制开始并且您在团队中工作时,这一点更为重要,因为合并冲突会减少。
想象一下,如果 Chrome 只是一个单一的 .cpp
文件。即使对它进行最微不足道的更改也需要重新编译整个程序,即使在装备精良的机器上也需要 6-12 小时。相比之下,编译单个源文件并重新链接只需几分钟。
在实践中,每个源文件通常有一个 class 和相应的头文件。函数在逻辑上组合成集合,每个集合在它们自己的一对 header/source 文件中。
人的大脑一次无法容纳太多信息,因此我们将事物分解成更小、合乎逻辑且连贯的部分。
好的。因此,一个 main.cpp 包含程序中的数十个或数百个或数千个文件,所有文件都在 header 个合理大小的文件中实现,每个文件涵盖一个概念或聚合更多 header 个文件应该一个概念过于宽泛,无法在单个 header 中轻松描述 1。问题解决了吧?是的。但这只是一个问题。
资源消耗情况如何?
在继续之前阅读 How does the compilation/linking process work? 很有帮助。
在预处理过程中,每个包含语句都被包含文件的内容替换。结果是一个庞大的文件被送入编译器。那会占用很多内存。此外,如果一个文件包含 headers 的所有内容,那么每次您进行更改时,无论多么小,都需要构建这个文件。它将包括项目的 header 的价值,并且一次更改会导致所有文件重新编译。这非常耗时。
内存越来越便宜,因此限制第一个资源并不像 1970 年代发明所有这些时那么重要2。仍然不时地抬起它丑陋的头。这就是为什么我在又大又胖的 PC 上交叉编译而不是直接在 Raspberry Pi.
上构建代码的原因
时间并没有变得更便宜。从来没有,永远不会。
但是,如果您遵循最佳实践并且 header 包含一个接口(它做什么)而不是一个实现(它如何做),您会发现 header 没有变化不大。大部分时间发生变化的是实施文件中的 how-to 细节。一个小的更改仅限于提供更改行为的一个实现文件。可能这是唯一需要重新编译的文件。从每次一个文件到每次一个或两个文件,这是一个巨大的进步。
在极少数情况下界面会发生变化,好吧,你接受它。
1 因为这就是代码:对行为的描述。它不是供计算机执行的指令列表——那是编译器的输出——它是对程序行为的描述。编译器的工作就是把你的描述变成指令。
2 这也解释了为什么在最近创建的语言中构建要简单得多3。在 1/2 K ram 和以千赫兹为时钟的 CPU 的辉煌岁月里,他们没有因为必须让事情正常工作而遗留下来的包袱。他们也从很多错误中吸取教训。
3 无论如何,对于最终用户。现代构建系统的后端是一些疯狂的代码,伙计。
我最近开始学习 makefile,我正在观看的视频包括两个源文件和一个头文件的编译。在两个源文件中都包含的头文件中定义了一个 class。 class 的方法在一个源文件中定义,并在第二个源文件 (main.cpp) 中被调用。为什么我需要 2 个源文件 (.c/.cpp)?
每次您对头文件进行任何更改时,每个包含它的文件都必须重新编译,但是如果您更改源文件只有该源文件需要重新编译。
当处理一个非常大的项目时,这可以显着减少编译时间。由于您的大部分工作通常是在实现方面完成的,其中对头文件的修改不如对源文件的修改频繁,因此大多数重新编译将被本地化。
出于可导航性的原因,您还需要将源代码拆分成单独的文件。处理一个 2000 多行的源文件比处理 10 个文件(每个文件大约 200 行)要麻烦得多。当版本控制开始并且您在团队中工作时,这一点更为重要,因为合并冲突会减少。
想象一下,如果 Chrome 只是一个单一的 .cpp
文件。即使对它进行最微不足道的更改也需要重新编译整个程序,即使在装备精良的机器上也需要 6-12 小时。相比之下,编译单个源文件并重新链接只需几分钟。
在实践中,每个源文件通常有一个 class 和相应的头文件。函数在逻辑上组合成集合,每个集合在它们自己的一对 header/source 文件中。
人的大脑一次无法容纳太多信息,因此我们将事物分解成更小、合乎逻辑且连贯的部分。
好的。因此,一个 main.cpp 包含程序中的数十个或数百个或数千个文件,所有文件都在 header 个合理大小的文件中实现,每个文件涵盖一个概念或聚合更多 header 个文件应该一个概念过于宽泛,无法在单个 header 中轻松描述 1。问题解决了吧?是的。但这只是一个问题。
资源消耗情况如何?
在继续之前阅读 How does the compilation/linking process work? 很有帮助。
在预处理过程中,每个包含语句都被包含文件的内容替换。结果是一个庞大的文件被送入编译器。那会占用很多内存。此外,如果一个文件包含 headers 的所有内容,那么每次您进行更改时,无论多么小,都需要构建这个文件。它将包括项目的 header 的价值,并且一次更改会导致所有文件重新编译。这非常耗时。
内存越来越便宜,因此限制第一个资源并不像 1970 年代发明所有这些时那么重要2。仍然不时地抬起它丑陋的头。这就是为什么我在又大又胖的 PC 上交叉编译而不是直接在 Raspberry Pi.
上构建代码的原因时间并没有变得更便宜。从来没有,永远不会。
但是,如果您遵循最佳实践并且 header 包含一个接口(它做什么)而不是一个实现(它如何做),您会发现 header 没有变化不大。大部分时间发生变化的是实施文件中的 how-to 细节。一个小的更改仅限于提供更改行为的一个实现文件。可能这是唯一需要重新编译的文件。从每次一个文件到每次一个或两个文件,这是一个巨大的进步。
在极少数情况下界面会发生变化,好吧,你接受它。
1 因为这就是代码:对行为的描述。它不是供计算机执行的指令列表——那是编译器的输出——它是对程序行为的描述。编译器的工作就是把你的描述变成指令。
2 这也解释了为什么在最近创建的语言中构建要简单得多3。在 1/2 K ram 和以千赫兹为时钟的 CPU 的辉煌岁月里,他们没有因为必须让事情正常工作而遗留下来的包袱。他们也从很多错误中吸取教训。
3 无论如何,对于最终用户。现代构建系统的后端是一些疯狂的代码,伙计。