在使用 struct 关键字自定义类型的时候,通常我们希望自定义的类型能够在不同的源文件中共享,在这种情况下,我们需要把自定义的类型组织成头文件的形式,一般情况下,头文件的文件名与我们所定义的类型的名字相同,头文件通常还会多一个以 .h 结尾的后缀

在自定义类型的时候,可能还会用到其他头文件的功能,在这种情况下,需要在自定义类型的头文件中包含另一个所用到的头文件,在自定义类型完成后,当源程序用到这种自定义的类型的时候,包含与之相关的头文件,就隐含地包含了定义这种自定义类型时用到的另一个头文件,而在源程序中,可能用到的自定义类型的数据成员与自定义类型时用到的另一个头文件有关,则又需要在源程序中包含另一个头文件一次,也就是说,源程序对于自定义类型中用到的头文件包含了两次,那么,怎么确保这两次包含都能正确地工作而不影响程序最后的结果呢?C++ 中使用预处理器来避免这种冲突。

有一种预处理功能比较常见,就是我们在源程序编码的过程中使用的 #include 指令,这就是一种预处理,预处理器在编译源程序之前执行,可以部分地改变我们编写的程序,当预处理器看到 #include 时就会用指定的头文件的内容代替 #include,C++ 中还会用到的另一种预处理功能就是头文件保护符,头文件保护符依赖于预处理变量,用 #define 指令将某个名字设定为预处理变量,预处理变量有两种状态:已定义和未定义,有两种指令用来检测预处理变量的状态:#ifdef#ifndef,当预处理变量已定义时 #ifdef 为真,当预处理变量未定义时 #ifndef 为真,当为真时代码可以继续执行,直到遇到#endif 指令为止,下面在定义 Sales_data 类型时用到了头文件保护符:

1
2
3
4
5
6
7
8
9
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
#endif

预处理变量无视 C++ 语言的作用域规则,第一次包含 Sales_data.h 时,#ifndef 的检查结果为真,程序一直执行直到遇到 #endif,当再次包含 Sales_data.h 时, #ifndef 的检查结果为假,程序不再往下执行,这样,就能很好地处理多次包含同一个头文件所引起的冲突,预处理变量的名字要求唯一,一般与对应的头文件同名且全部采用大写的形式避免冲突,需要注意的是:

头文件即使(目前还)没有被包含在任何其他头文件中,也应该设置保护符,没必要太在乎你的程序到底需不需要