头文件与源文件

一般我们将大量公用的代码段存放在头文件中,然后使用其他源文件引用这个头文件,从而使得很多代码段不需要在每个源文件中都书写一遍。一种推荐的策略是这样的,将整个项目/程序包分为三个部分:

  • 头文件(.h):包含结构体等数据类型的声明,类的框架、函数原型等等抽象而公用的部分
  • 源代码文件(.cpp):负责头文件中的抽象部分的具体化,如函数的主体、类的实现等
  • 源代码文件(.cpp):负责调用头文件中的函数以及实例化类模型等从而完成项目逻辑

头文件中不要包含函数的定义或者变量的声明,头文件中一般只包含如下的部分:

  • 函数原型
  • 使用 #define 或 const 定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

头文件通常的书写架构如下,例如我们创建了一个命名为 headFile.h 的头文件:

1
2
3
4
5
6
#ifndef HEADFILE_H_
#define HEADFILE_H_

// the head file

#endif

对头文件与源代码文件编译时是单独编译的,编译器会创建每个源代码文件的目标代码文件,生成 .o 后缀的文件,然后使用连接器将两个部分连接起来,将库代码和启动代码合并,生成一个 .out 后缀的文件,进而生成 .exe 后缀的可执行文件。

多文件编译的过程
多文件编译的过程

名称空间

名称空间可以理解为为了区分不同的库类不同的厂商的代码中所包含的同命名的变量、函数等来问题的。例如我的程序包含了两个厂家开发的库,但这两个库中都有一个名为 List 的类,为了区分使用哪个厂家的类,就需要这两个厂家将自己的 List 类封装在自己的名称空间中,从而方便区分。

名称空间的创建

我们自己也可以创建名称空间,例如我们声明一个int类型的变量myInt,将其放在我们定义的一个名称空间MHZ中,可以这样写:

1
2
3
4
namespace MHZ
{
int myInt;
}

名称空间的使用

如果我想在我的程序中调用名称空间MHZ中的myInt变量,需要使用该名称空间,有以下的一些方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 编译使用整个名称空间
// 这种方式最不安全
// 因为会将整个名称空间的内容全部编译使用
// 很容易出现不同名称空间重名的问题
using MHZ;
cout << myInt;

// 指定编译使用名称空间中的某个变量/函数等
// 这种方式相对安全
// 使用哪个因素就声明那个因素
using MHZ::myInt;
cout << myInt;

// 在调用的时候指明名称空间
// 这种方式虽然繁琐,但是最不容易出错
cout << MHZ::myInt;