C++中的头文件,c++包含所有头文件的头文件
1.c编译模式
一般来说,在C程序中,只有两种类型的文件:——.cpp文件和。h文件。其中,的。cpp文件叫C源文件,所有的C源代码都放在里面;的。h文件称为C头文件,其中也包含C源代码。
c语言支持“单独编译”。也就是说,一个程序的所有内容都可以分成不同的部分,放在不同的地方。cpp文件。一切都在。cpp文件相对独立,编译时不需要和其他文件通信。它只需要在编译成目标文件后与其他目标文件链接。比如在文件a.cpp中定义了一个全局函数“void a() {}”,但是这个函数需要在文件b.cpp中调用,即便如此,文件a.cpp和文件b.cpp也不需要知道对方的存在,而是可以分别编译,编译到目标文件中然后链接,整个程序就可以运行了。
这是如何实现的?从编程的角度来说,很简单。在文件b.cpp中,在调用“void a()”函数之前,声明函数“void a()”,就行了。这是因为编译器在编译b.cpp时会生成一个符号表,不能定义的符号如“void a()”会存储在这个表中。再次链接时,编译器将在其他目标文件中查找该符号的定义。一旦找到,程序就能顺利生成。
注意,这里提到两个概念,一个是“定义”,一个是“声明”。简单来说,“定义”就是完整的描述一个符号:是变量还是函数,返回什么类型,需要什么参数等等。而“声明”只是声明这个符号的存在,也就是告诉编译器这个符号是在其他文件中定义的。我先在这里用,当你链接的时候,去别的地方看看到底是什么。定义的时候要完全按照C语法定义一个符号(变量或者函数),而声明的时候只需要写出这个符号的原型。请注意,一个符号可以在整个程序中声明多次,但只能定义一次。试想一下,如果一个符号有两种不同的定义,编译器应该听谁的?
这种机制给C程序员带来了很多好处,也引出了一种编程的方法。考虑一下,如果有一个非常常见的函数“void f() {}”会在许多。cpp文件,那么我们只需要在一个文件中定义这个函数,在其他文件中声明。一个函数很好处理,一句话就能声明。但是,如果有很多函数,比如很多数学函数,有几百个呢?你能保证每个程序员都能完整准确的把所有函数的形式写下来,写出来吗?
二、什么是头文件?
显然,答案是不可能的。但是有一个非常简单的方法可以帮助程序员省去记住那么多函数原型的麻烦:我们可以先把上百个函数的声明都写在一个文件里,等程序员需要的时候再复制到源代码里。
这个方法可行,但还是太麻烦,太笨拙。然后,头文件就可以发挥作用了。所谓头文件,其实它的内容和。cpp文件,这是c的源代码。但是头文件不需要编译。我们将所有的函数声明放在一个头文件中。当某个。cpp源文件需要它们,它们可以包含在此。cpp文件,从而将它们的内容合并到。cpp文件。当。cpp文件的编译,包括这些的作用。h文件被播放。
举个例子,假设所有的数学函数只有两个:f1和f2,那么我们把它们的定义放在math.cpp:
/* math.cpp */
双f1()
{
//在这里做点什么.
返回;
}
双f2(双a)
{
//在这里做点什么.
返回a * a
}
/*结束math.cpp */
并将“这些”函数的声明放在头文件math.h中:
/* math.h */
双f1();
双F2(double);
/*数学结束. h */
在另一个文件main.cpp中,我想调用这两个函数,所以我只需要包含头文件:
/* main.cpp */
#包含“math.h”
主()
{
int number 1=f1();
int number 2=F2(number 1);
}
/* main . CPP的结尾*/
这是一个完整的程序。应该注意的是。h文件不需要写在编译器的命令之后,但是必须在编译器能找到的地方(比如和main.cpp在同一个目录下)。Main.cpp和math.cpp可以分别编译生成main.o和math.o,然后把两个目标文件链接起来,程序就可以运行了。
三。#包括
#include是C语言的一个宏命令,在编译器编译它之前,也就是预编译的时候就会起作用。#include的作用是将后面写的文件内容完整地、逐字地包含到当前文件中。值得一提的是,它没有其他功能或副作用。它的作用是用写在它后面的文件内容替换它出现的每一个地方。简单的文字替换,没别的。因此,在编译之前,main.cpp文件中的第一句话(#include math.h )将被替换为math.h文件的内容。也就是说,在编译过程的开始,main.cpp的内容发生了变化:
/* ~main.cpp */
双f1();
双F2(double);
主()
{
int number 1=f1();
int number 2=F2(number 1);
}
/*结束main . CPP */
不多不少,刚刚好。同样,如果有许多其他的。除了main.cpp之外的cpp文件也使用f1和f2函数,那么它们都只需要在使用这两个函数之前编写一个#include math.h。
4.头文件应该写什么?
通过上面的讨论,我们可以知道头文件的目的是被其他。cpp。它们不参与编译,但实际上,它们的内容是在几个。cpp文件。通过“定义只能有一次”的规则,我们很容易得出这样的结论:头文件中应该只放变量和函数的声明,而不是它们的定义。因为头文件的内容实际上会被导入到许多不同的。cpp文件,它们都会被编译。当然,放宣言也没问题。如果你把定义,它相当于一个符号(变量或函数)的定义出现在多个文件中。即使这些定义都是相同的,编译器这样做也是非法的。
因此,应该记住在。h头文件中,只能有变量或函数的声明,不能有定义。也就是只能写在头文件里像:extern int a;和void f();这句话。这些是陈述。如果你写int a;或者像void f() {}这样的句子,那么一旦这个头文件被两个或多个。cpp文件,编译器会立即报告错误。(关于extern,我们之前讨论过,这里就不讨论定义和声明的区别了。)
然而,这条规则有三个例外。
首先,const对象的定义可以写在头文件中。因为全局const对象隐式识别没有extern的声明,所以它只在当前文件中有效。如果将这样一个对象写入头文件,即使它包含在其他几个?cpp文件,这个对象只在包含它的文件中有效,对其他文件是不可见的,所以不会导致多重定义。同时,因为这些中的对象。cpp文件包含在一个头文件中,这确保了const对象的值在这些。cpp档案也是一样,可以一举两得。同样,静态对象的定义也可以放到头文件中。
第二,内联函数的定义可以写在头文件中。因为内联函数需要编译器在遇到它的地方根据它的定义进行内联扩展,而不是像普通函数一样在链接之前声明(内联函数不会被链接),所以编译器需要在编译时看到内联函数的完整定义。如果内联函数只能像普通函数一样定义一次,那就很难了。因为在一个文件里没事,所以我可以把内联函数的定义写在开头,这样以后用的时候就能看到定义了;但是如果我在其他文件中使用这个函数呢?这几乎不是一个好的解决方案。所以C规定一个内联函数可以在一个程序中定义多次。只要内联函数在. cpp文件中只出现一次,并且该内联函数的定义在所有文件中都是相同的。cpp文件,可以编译。显然,将内联函数的定义放在头文件中是明智的。
第三,类的定义可以写在头文件中。当在程序中创建类对象时,只有当类的定义完全可见时,编译器才能知道类对象应该如何布局。因此,对类定义的要求与内联函数的要求基本相同。因此,将类的定义放在头文件中并将头文件包含在。用于此类的cpp文件。这里值得一提的是,类的定义包含数据成员和函数成员。在创建具体对象之前不会定义数据(分配空间),但是函数成员需要在一开始就定义,也就是我们通常所说的类的实现。通常,我们的方法是将类的定义放在头文件中,将函数成员的实现代码放在. cpp文件中。这是可能的,也是一个好办法。然而,还有另一种方法。也就是直接把函数成员的实现代码写到类定义里。在C的类中,如果在类的定义体中定义了一个函数成员,那么编译器会将这个函数视为内联的。因此,将函数成员的定义写入类定义体,并将它们一起放在头文件中是合法的。请注意,在类定义的头文件中编写函数成员的定义是非法的,但在类定义中却不是,因为这个函数成员此时不是内联的。一旦头文件被两个或多个。cpp文件,这个函数成员被重新定义。
动词(verb的缩写)头文件中的保护措施
考虑一下,如果头文件只包含声明语句,那么它可以被同一个。cpp文件多次,因为声明语句是无限的。然而,上面讨论的头文件中的三个例外也是头文件的常见用法。那么,一旦头文件中出现上述三种异常中的任何一种,如果被a. cpp多次包含,就会出现大问题。因为这三个异常中的语法元素可以在多个源文件中定义,但是在一个源文件中只能出现一次。想象一下,如果a.h包含A类的定义,b.h包含B类的定义,由于B类的定义依赖于A类,所以b.h也#包含a.h .现在有一个同时使用A类和B类的源文件,所以程序员在这个源文件中同时包含A.H和B.H .此时问题出现了:A类的定义在这个源文件中出现了两次!所以整个程序都编译不出来。你可能觉得是程序员的失误。他应该知道B.H .含有a.h——,但实际上他不应该。
在条件编译中使用 #define 可以很好地解决这个问题。在头文件中,通过#define和#ifndef的条件编译来定义名称.#endif,编译器可以根据名称是否定义来决定是否编译头文件中的以下内容。虽然这个方法很简单,但是你必须记住写头文件。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。