C++ C++;包括文件

C++ C++;包括文件,c++,include,C++,Include,我知道在头文件中提供定义允许其他文件引用它们,然后链接器将所有对象文件添加到一起,稍后提供定义 这只是为了我们可以在其他库中重用实现吗 如果我们不使用头文件或滥用头文件,并将所有代码放在头文件中,即完整的实现,会发生什么?编译每个文件时是否需要更长的时间,因为完整定义需要在对等文件的基础上编译?它是否会导致链接器出现问题,因为同一实现可能有多个编译版本 这是如何与模板一起工作的 Blair历史上或多或少需要将源文件和头文件中的代码分开。它允许编译器分别编译大型项目的代码单元,因为整个项目太大,无

我知道在头文件中提供定义允许其他文件引用它们,然后链接器将所有对象文件添加到一起,稍后提供定义

这只是为了我们可以在其他库中重用实现吗

如果我们不使用头文件或滥用头文件,并将所有代码放在头文件中,即完整的实现,会发生什么?编译每个文件时是否需要更长的时间,因为完整定义需要在对等文件的基础上编译?它是否会导致链接器出现问题,因为同一实现可能有多个编译版本

这是如何与模板一起工作的


Blair

历史上或多或少需要将源文件和头文件中的代码分开。它允许编译器分别编译大型项目的代码单元,因为整个项目太大,无法立即编译

它还允许缓存未更改的编译单元,这进一步缩短了编译时间

因此,从技术上讲,编译器可以看到一整套不同的编译单元。如果您提供了同一定义的多个实现,编译器将无法告诉您。但一旦链接器将编译后的单元打包成一个可执行文件,它就会被它绊倒


今天,像C#或Java这样的“现代”语言摆脱了这种历史限制。但是C++是一种复杂的语言,甚至对于编译器来说,这些优点仍然是有意义的。

< P>在头文件中放置的任何代码都是在每次编译目标包含该头文件时编译的,所以至少是的,编译需要更长的时间。不过,一般来说,这并不是一个重大问题

假设您有以下标题:

int f_header() {
    return 0;
}
以及这些源文件:

/* a.cpp */
#include "myheader.h"
int f_a() {
    return f_header();
}


/* b.cpp */
#include "myheader.h"
int f_b() {
    return f_header();
}
现在,当您编译这些源代码时,编译将成功。但是,当您链接程序时,链接器将失败,因为您将提供函数
f_header()
两次


模板是一种特殊情况,因为它们实际上不是代码,而是为基于不同类型生成的代码提供模板。虽然当多个源文件使用相同类型的源文件时会出现重复,但编译器能够为您处理这种特殊情况。

编译器一次编译一个文件。编译器正在解析的源文件可能包含其他文件,但在预编译阶段,所有这些包含的文件都被“展平”为单个源文件,并且编译器“看到”单个代码实体

由于上述原因,对于编译器来说,代码是在.cpp文件还是在.h文件中并不重要,因为编译器不会进行这种区分。但是,如果某个函数定义或某个变量定义在.h文件中,则问题会在链接阶段出现

链接器看不到源文件,但它确实看到了目标文件(.obj),这是编译器生成的结果。链接器知道每个对象文件中的每个函数和每个变量,链接器的任务是在单个可执行文件中将它们绑定(或链接)在一起。若在不同的对象文件中有几个同名的变量或函数,那个么链接器将不知所措,它将声明一个错误。可以使用一个选项强制链接器忽略所有额外的变量和函数,但不能轻易使用该选项。两个同名函数实际上可能完全不同,忽略其中一个函数将导致各种错误


另一方面,模板根本不是函数。它们是实际功能的蓝图。当编译器遇到模板时,不会发生任何实质性的问题。只有当模板被实例化为实际函数时,编译器才会生成这样的函数。正因为如此,模板可以很好地驻留在头文件中。

如果每个文件都是一个单独的翻译单元,并且链接会导致像您提到的那样的问题,那么它如何知道如何处理模板,因为您可能在单独的翻译单元中使用相同的实例。正如我所说的——模板是编译器知道的一种特殊情况。它所做的类似于“静态”函数——它生成代码,使其符号在翻译单元之外不可用——使链接器无法命名该代码。