11.10 头文件

优质
小牛编辑
129浏览
2023-12-01

在结构体定义中声明函数,稍后再定义函数,这看起来是一件麻烦事。任何时候你要改变一个函数的接口,都需要在两个地方做修改,即使只是做了很小的变动,比如把一个参数声明为const。

尽管如此,这种麻烦是有理由的,我们能够把结构体定义和函数分离到两个文件中:头文件包含着结构体定义,而实现文件包含着函数。

头文件通常和实现文件同名,但后缀是.h而不是.cpp。对于我们一直看的例子,头文件名为Time.h,它包含以下内容:

struct TIme {
    // 实例变量
   int hour, minute;
   double second; 

   //构造函数
   Time (int hour, int min, double secs);
   Time (double secs);

  //修改器
   void increment (double secs);

  //函数
   void print () const;
   bool after (const Time& time2) const;
   Time add (const Time& t2) const;
   double convertToSeconds () const;
};

请注意,我们并不需要在结构体定义中给每个函数名前面包含前缀Time::。编译器知道我们声明的函数是Time结构体的成员。

Time.cpp包含了成员函数的定义(为节省篇幅,我已经省去了函数体):

#include <iostream.h>
#include "Time.h"

Time::Time (int h, int m, double s)  ...

Time::Time (idouble secs)  ...

void Time::increment ( double secs)  ...

bool Time::after (const Time& time2) const ...

Time Time::add (const Time& t2) const ...

double Time::convertToSeconds () const ...

在本例中,Time.cpp中的定义与Time.h中声明的顺序相同,这并非必要。

另一方面,有必要使用include语句将头文件包含进来。这样一来,当编译器读取函数定义时,它能足够了解结构体,便于检查代码并捕获错误。

最后,main.cpp包含了函数main,以及我们需要的非Time结构体的成员的函数(本例中没有):

#include <iostream.h>
#include "Time.h"

void main() 
{
  Time currentTime (9, 14, 30.0);
  currentTime.increment (500.0);
  currentTime.print ();

  Time breadTime (3, 35, 0.0);
  Time doneTime = currentTime.add (breadTime);
  doneTime.print ();

  if (doneTime.after (currentTime)) {
      cout << "The bread will be donw after it starts." <<endl;
  }
}

再一次,main.cpp必须包含头文件。

把如此小的程序分成三部分的好处也许并不明显。其实,大部分优点会在我们处理更大的程序时体现出来:

重用:当你写了个类似于Time的结构,你也许会发现它在多个程序中都有用。通过把Time的定义从main.cpp中分离出来,在其它程序中包含Time结构会变得容易。

管理交互:随着系统变大,组件之间的交互数量快速增加,变得难以管理。通过从使用它们的程序中分离出Time.cpp这样的模块,可以最小化这些交互。

独立编译:单独的文件可以被独立编译,之后链接到一个程序中。其中的细节依赖于你的编程环境。随着程序规模变大,独立编译能节省很多时间,由于你通常每次只需要编译少数一些文件。

对于类似本书这样的小程序来说,分割程序并没有多大好处。但你最好知道这个特性,特别是它解释了我们写的第一个程序中出现的一个语句:

#include <iostream.h>

iostream.h是一个包含着cin和cout声明以及操作它们的函数的头文件,当编译程序时,你需要该头文件中的信息。

这些函数的实现存储在一个库中,有时候被称为"标准库",它能自动链接到你的程序中。好处在于当你编译程序时,你不需要每次都重新编译库。大多数情况下,库不会改变,因此没有理由重新编译它。