1.9 头文件

from http://www.learncpp.com/cpp-tutorial/19-header-files/

在程序中,.cpp扩展的文件并不是唯一一种常见的文件。另一种文件称为头文件,有时被称为include file。都文件基本都有一个.h扩展名。头文件的目的是将其它文件要用到的声明整合到一起。

 

标准库头文件的使用

看一下下面的程序:

   1: #include <iostream>
   2: int main()
   3: {
   4:     using namespace std;
   5:     cout << "Hello, world!" << endl;
   6:     return 0;
   7: }

 

这个程序使用cout将Hello, world!输出到控制台的屏幕中。但是,你的程序从没有定义cout,编译器如何知道cout是什么呢?答案是,cout在头文件iostream中已经声明过了。当我们使用#include <iostream>时,我们已经告诉编译器定位然后读取头文件iostream中的所有声明。

记住头文件中往往只包含了声明。它们并不定义具体的实现,而且你已经知道如果连接过程中,不能够找到你使用的东西的定义,你的程序在链接时将会出错。那么如果cout不是在iostream头文件中定义的,它实际又在哪里实现的呢?它是在运行库支持下实现的,在链接的过程中运行库会自动的链接到你的程序。

1.9 头文件 

库是能够反复被很多程序使用的代码的集合。更据代表性的来讲,一个库包含了头文件,头文件中包含了库里的任何能够被用户使用的东西的声明,预编译对象将所哟逇代码用机器码进行了实现。在windows中库的扩展名通常是 .lib 或 .dll,在Unix中,通常为 .a 或 .so。为什么要进行预处理呢。首先,由于库很少改变,它们通常不需要反复编译。每次都编译它们会浪费很多时间。其次,由于预编译对象是机器语言,它阻止人改变代码,出于对知识产权的保护,对于那些不想让别人知道源代码的人来说是很重要的。

 

编写你自己的头文件

现在让我们回到前面讨论过的问题。我们有两个文件add.cpp和main.cpp

add.cpp:

   1: int add(int x, int y)
   2: {
   3:     return x + y;
   4: }

 

main.cpp

   1: #include <iostream>
   2:  
   3: int add(int x, int y); // forward declaration using function prototype
   4:  
   5: int main()
   6: {
   7:     using namespace std;
   8:     cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
   9:     return 0;
  10: }

这里使用了前置声明,编译时编译器能够知道使用的add具体是什么。前面提到过,在每次使用函数前写前置声明是一件很烦人的事情。

头文件能够较少我们这方面的压力。一个头文件只需要写一次,就能够在很多源文件中使用。这也有助于使得函数原型改变时造成的改动减小(如,增加一个参数)。

书写我们的头文件是相当简单的。头文件包含两部分。第一部分是头文件保护,在预处理器一节中会详细介绍。第二部分是实际的内容,用来声明我们想要其他源文件能够看到的函数。我们的头文件的扩展名也是.h。

add.h:

   1: #ifndef ADD_H
   2: #define ADD_H
   3:  
   4: int add(int x, int y); // function prototype for add.h
   5:  
   6: #endif

为了main中能够使用头文件,必须将它包含进去。下面是一个新的main.cpp:

   1: #include <iostream>
   2: #include "add.h" // this brings in the declaration for add()
   3:  
   4: int main()
   5: {
   6:     using namespace std;
   7:     cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
   8:     return 0;
   9: }

当编译器读到#include "add.h"这一行的时候,它将add.h中的内容都复制到当前的文件中。因为我们的add.h中包含了函数add的原型,作为函数add的前置声明。

因此,我们的程序能够正确的编译和链接。

1.9 头文件

你也许会很好奇为什么对于iostream我们使用了尖括号,对于add.h我们使用了双引号。原因是尖括号告诉编译器我们包含的头文件是编译器包含的。双引号告诉编译器头文件是我们提供的,这告诉编译器首先在当前包含源文件的文件夹中寻找头文件。

 

规则:包含与编译器结合在一个的头文件使用尖括号。包含其他的头文件使用双引号。

 

另一个通常问的问题是“为什么iostream没有.h扩展名”。因为iostream.h和iostream是不同的,要解释它需要一个简短的历史背景。

当C++最初被创建的时候,标准运行库中的所有文件都是以.h结尾的。一直都是始终如一的,cout和cin的原来的版本也都存在于iostream.h中。当语言被ANSI委员会标准化后,它们决定将所有运行库的函数移到std命名空间下(这是一个好想法)。但是,又引起了一个问题:如果把所有的函数移到std命名空间,以前的程序都将不能运行。

为了解决这么问题,一系列新的没有.h后缀的具有相同名称的头文件被推荐使用。新的头文件中的所有的内容都是在std命名空间内的。这样,包含#include <iostream.h> 旧的程序不必要被重写,新的程序可以使用#include <iostream>。

当你使用标准库的头文件的时候确保你没有使用.h扩展的头文件。否则,你将会使用一个被弃用的不再将被支持的版本。

作为边注,标准库中的很多头文件没有不具有不带.h的版本,只有带.h 的版本。对于这些文件,使用.h的版本是可以的。很多这类库向后兼容标准C程序,C程序不支持命名空间。当你写你的头文件的时候,它们通常是以.h结尾的,你没有必要将你的代码放到std命名空间中。

规则: 如果库中存在,就是用使用不带.h的版本,通过std命名空间访问功能。如果不存在不带.h扩展名的版本,或是你自己建立的头文件,使用.h的版本。

你可能感兴趣的:(文件)