在结构体定义中声明函数,稍后再定义函数,这看起来是一件麻烦事。任何时候你要改变一个函数的接口,都需要在两个地方做修改,即使只是做了很小的变动,比如把一个参数声明为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声明以及操作它们的函数的头文件,当编译程序时,你需要该头文件中的信息。
这些函数的实现存储在一个库中,有时候被称为"标准库",它能自动链接到你的程序中。好处在于当你编译程序时,你不需要每次都重新编译库。大多数情况下,库不会改变,因此没有理由重新编译它。