(C和C ++)使用的基本 编译模型 是:
- 您将要在源文件之间共享的 declarations 放入头文件。然后,需要访问这些声明的源文件包括这些头文件。
- 编译时,预处理器将(递归地)将
#include
指令替换为包含的文件。结果称为编译单元。
- 然后,编译器将这些编译单元一次编译到目标文件中。 (注意:编译器总是只看到一个翻译单元。它无法访问其他翻译单元中声明和定义的内容。)基本上,目标文件是易于编译的代码,但所有参考文献到编译单元之外的符号(函数,变量)仍然是符号。
- 然后,链接器将传递所有目标文件,并将对这些目标文件中的符号的引用链接到符号“definitions”,如果一切顺利,则吐出可执行文件。
醇>
在C ++中,这在实践中往往有点复杂(特别是由于内联和模板,还有链接时代码生成等功能),但这是基本原则。
其含义是:
- 源文件通常只包含头文件
由于整个预处理器魔术只是一个简单的文本替换引擎而不知道(C或C ++)及其在上述过程中的目的,因此可以弯曲和滥用来做其他事情。在这方面,包括源文件有时已经完成了一些目标。但这种情况很少见。
- 符号可以根据需要经常声明,但必须一次定义
如果链接器找不到已声明的符号(因此编译器已接受对它的引用),它将向您的脸部吐出一条令人讨厌的错误消息。如果它找到多个定义,它也会这样做。
- 编译器不关心声明是来自预处理器包含的头还是直接写入源文件。
但是,如果您将声明直接写入源文件,则编译器无法“查看”其他翻译单元,则无法警告您它们已过期。如果将声明放入头文件中,则更容易使它们与相应的定义保持同步,并且编译器通常甚至可以诊断它们是否不匹配。
- 任何具有多个源文件的项目只能通过多个编译运行(每个源文件一个)构建,链接器将链接生成的目标文件。但通常情况下,IDE隐藏在项目管理之后
如果更改直接或间接在许多源文件中使用的标头,则必须重新编译大部分项目。如果更改源文件,则只需重新编译该源文件(当然,重新链接可执行文件)。
您应该只包含头文件,但在链接时,您应该传递给实现这些功能的编译器对象文件名。
您只在要使用这些功能的文件中包含标题。在链接期间,链接器将查找与标头定义匹配的目标文件,因此您必须确保编译器也可以看到它(通过使源文件存在)。
另一方面;如果确实也包含源文件,则很可能会出现多符号定义错误。所以,不要这样做。
仅在其他源文件(函数的客户端)中包含头文件。
但是,在构建时,您需要将所有源文件输入到编译器/链接器。