2010-10-07 61 views
3

这个基本stucture是一个脑死亡的新手问题,但在这里有云:的C/C++项目(头文件和cpp文件)

是什么决定了什么样的文件被包括在C/C++项目?

我的理解是,编译器以其中包含main()的文件开始,该文件将包含#include以获取包含#include到其他h文件的各种h文件,等等,直到所有内容都包含在项目。

我的问题:

什么是h文件和同名的cpp文件之间的关系?我的意思是,我当然明白,代码方面他们需要彼此,并且cpp文件总是(几乎总是?)#include的h文件,但是从编译器的角度来看,重要的是它们具有相同的名称或是这只是一个约定?我可以包含额外的cpp文件而不包含相应的h文件吗?

此外,当项目建立并链接时,它如何知道哪些cpp/h文件要构建目标文件?它是否会从其中包含“main()”的cpp文件开始,并继续执行#include,直到它具有所需的所有内容并构建所有这些内容,还是只构建用户在生成文件或IDE项目文件?

最后,当链接器最终出现并链接所有目标代码以创建一个可执行文件时,是否有特殊的顺序来排列所有内容?

任何帮助,提示,解释赞赏.. 谢谢!

--R

回答

1

将文件视为分离代码的简单方法,使其更具可重用性,更易于维护。

您可以轻松地将整个应用程序放在一个大的喇叭源文件中,但是您可能会发现该文件会变得很大,导致编译器抱怨它(或者至少需要很长时间才能编译它) 。

通常,您应将应用程序的一部分(如通用数据库访问层)分配到单独的源文件(如db.cpp)中,并使用其API创建db.h文件。 db.cpp与其他所有需要调用db.cpp中的函数的文件所使用的文件没有太大关系。它可以包含在db.cpp中,但它往往主要是公布有关db代码的信息。至于环境如何计算出编译/链接的东西:你往往会有一些项目(makefile,IDE项目文件等),它列出了你想编译的所有程序(通常不是头文件文件)。

环境将编译每个源文件,它已被告知要生成一个目标文件 - 该过程的一部分是将包含的头文件合并到每个源文件中,以生成一个编译或翻译单元 - 该单元基本上包含头文件的源文件包含在#include所在的位置。

然后环境会链接所有的目标文件以形成可执行文件。请记住,这个过程有一些变化,例如后期(动态)链接。请参阅here以获得对此的描述。

1

网络上的一个小狩猎会变成很多你的答案。这里只有两个:http://www.psgd.org/paul/docs/cstyle/cstyle02.htm

http://www.cs.utexas.edu/~lavender/courses/EE360C/lectures/lecture-02.pdf

第二个是相当不错的。

我也推荐C++编程语言第3版。有一个关于文件组织的很棒的部分。

至于编译器的功能,最好在单独的文章中解释。简而言之,每个cpp文件都被编译成一个翻译单元(目标代码),然后链接器将所有内容连接到最终的可执行文件中。

0

头文件实际上是一个类及其所有成员属性和函数的前向声明,这基本上会使您的类更具可重用性和更易于访问。把它看作是一个没有实现的接口,因此使用它的任何人都不必担心该类的来源。从我知道相应的h文件和cpp文件需要具有相同的名称。一个cpp文件不一定总是有一个对应的h文件,你可以将所有源代码放在一个cpp文件中,而不需要任何h文件,只要所有东西都正确实现并且原型正确,一切都应该正常工作。

0

您的分析基本上是正确的......所有包含的文件都被扩展到位,并且生成的代码(一个翻译单元)被编译到一个对象,库或应用程序中。即使只有像malloc(),socket(),file(),write()等东西提供的东西,任何非平凡的项目依赖于其他库中定义的符号(变量,函数)语言或操作系统的标准库。即使你不直接给他们打电话,他们也需要执行诸如new和iostream之类的东西。

当您自己的项目变得更大时,您还需要将您的功能划分为不同的对象或库,因为这会使该功能更具可重用性,可独立测试,并且意味着在某些代码更改之后,您只能重新编译那些被更改失效的对象将重新链接 - 这可能比重新编译整个项目中的每一个代码都快得多。

你的C++编译器创建对象(这可能会或可能不会有多余的接口和代码,使他们的库或应用程序)从翻译单元 - 这是你所提到的包括串连和CPP文件 - 可能进口并将其与来自现有静态库或您在编译器命令行中提到的其他对象的符号结合使用。

对于这些独立对象中的每一个,编译器都需要能够告诉新代码如何访问和使用包含的符号;头文件服务于这个目的,广告可用的对象内容。

实现(cpp)文件应该几乎总是首先包含它们的头文件,因为如果它正在构建的对象内容与使用该对象的代码稍后使用该对象进行编码的头文件广告内容之间存在一些差异,则编译器会抱怨期望。对于某些事情(比如类),必须在可以指定成员函数实现之前看到类声明,并且由于客户端代码需要类声明,因此在实际的实现中,实现需要包含头。 (我说cpp应该包含头第一个,因为如果头依赖于某些不包含自身的内容,那么编译器会发出抱怨。否则,如果说cpp包含std :: string头并且头使用它,但其他一些客户端代码试图包含头而不包含字符串,则编译将失败)。

实现文件可能包含其他实现文件,但不符合上述编译的一般划分,因此会混淆用于此惯例的人员。