在前面的示例中我们编译的都是单文件的程序,而对于大型程序来说,我们必须有效地将代码分别放在不同的文件中。编译器分别编译每个单独的文件,生成相应的.obj目标文件;链接器再将这些.obj目标文件和必要的.lib库文件链接,从而生成最后的可执行代码。
变量和函数都可以将声明(Declaration)与定义(Definition)分离。定义是一定要初始化的,而声明却只是让编译器知道现在有这样一个变量,接下来可以用了。一个文件A中定义的变量a也可能被另一个文件B使用,而这个文件B也需要在单独编译的时候知道这个变量a的信息,所以也需要声明。因此我们知道声明是可以存在多个的,但是带有初始值或函数体的定义只能有一个,不然编译器无法确定要使用的初始值或函数体到底是哪一个。接下来让我们看两个相关的示例:
动手写3.6.1
动手写3.6.1展示了不同文件中多次声明的情况。其中,我们可以看到num在3.6.1_1.cpp中被定义,而在3.6.1_2.cpp中被使用。在使用前我们先用声明语句让编译器知道要去别的文件中寻找定义,因此我们需要使用extern关键字来表明变量定义在别的文件中,或者也可以使用#include包含的方式。
需要注意的是,变量不能在同一文件中声明两次,因为没有初始化的变量声明会被解读为默认初值的定义,而函数的定义和声明在形态上就有明显的区别,在编译时不会产生歧义。
动手写3.6.2
动手写3.6.2展示了重定义的情况。num在两个文件中分别被定义,这会导致程序在链接的时候出现如图3.6.1所示的链接错误:
图3.6.1 多重定义(www.xing528.com)
上述的示例只适用于使用个别在其他文件中定义的变量的情况,在实际的大型程序中,我们可能有许多这样的变量,并且同一批变量和函数会被许多文件使用。对于这种情况,显然一个一个地声明就效率太低了,而且容易出错。这个时候我们就可以把变量和函数的声明放到头文件中,并在需要的文件中使用#include包含头文件。
不同于一般源代码文件的后缀“.cpp”,头文件(Header File)一般用“.h”作为后缀。头文件主要存放extern变量声明和函数声明两种声明,也可以放类定义、const常量定义和inline函数定义,这是因为这些定义重复出现也是没问题的,只要保证定义都相同就可以。按照惯例,我们一般都会把变量声明和函数声明对应的定义,以及类定义中成员函数的定义放在与头文件同名的cpp文件中,并包含头文件,这样一来,使用这些定义的用户文件只要包含头文件就不会重复地包含cpp,从而避免重定义错误。图3.6.2清晰地表现了这一思想:
图3.6.2 包含头文件示意图
我们可以看到,“1.cpp”和“2.cpp”想使用一些和数学相关的变量和函数,就只需要包含“math.h”,而如果函数的实现和变量的值有修改,在这两个文件中也不需要修改。接下来我们来看一个实际编写并使用头文件的示例:
动手写3.6.3
动手写3.6.3展示了头文件的使用。我们在头文件中定义了一个extern变量和一个const常数,并在相应的cpp中定义了extern变量的值;然后在main文件中包含了自定义头文件,并在main函数中使用了其中定义的变量。
我们之前说过,在文件中使用变量需要先声明,而示例的main文件中并没有变量声明,只有#include命令。这是因为#include的实际语义就是告诉编译器在预处理阶段把头文件的代码复制到#include命令所在的位置,然后实际编译的时候就有变量声明了。所以在这里,3.6.3_main.cpp的等价代码其实是:
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。