在编译器对源程序进行编译时,首先要由处理器对程序文本进行预处理。预处理器提供了一组编译预处理指令和预处理操作符。预处理指令实际上不是C++语言的一部分,它只是用来扩充C++程序设计环境。所有的预处理指令在程序中都以“#”来引导,每一条预处理指令单独占用一行,不要用分号结束。预处理指令可以根据需要出现在程序中的任何位置。
1. #include指令
#include
指令也称文件包含指令,其作用是将另一源文件嵌入到当前源文件中该点处。通常用#include
指令来嵌入头文件。文件包含指令有如下两种格式:
(1)#include<文件名>
按标准方式搜索,文件位于系统目录的include子目录下。
(2)#include"文件名"
首先在当前目录中搜索,若没有,再按标准方式搜索。
#include
指令可以嵌套使用。假设有一个头文件myhead.h,该头文件中又可以有如下的文件包含指令:
#include"file1.h"
#include"file2.h"
2. #define指令和#undef指令
预处理器最初是为C语言设计的,#define
曾经在C程序中被广泛使用,因为#define
可以完成的一些功能能够被C++引入的一些语言特性很好地替代。
在C语言中,用#define
来定义符号常量,例如下面预编译指令定义了一个符号常量PI的值为3.14:
#define PI 3.14
在C++中虽然仍然可以这样定义符号常量,但是更好的方法是在类型说明语句中用const修饰。
在C语言中,还可以用#define
来定义带参数宏,以实现简单的函数计算,提高程序的运行效率,但是在C++中这一功能已被内联函数取代。
#define
还可以定义空符号,例如:
#define MYHEAD_H
定义它的目的,仅仅表示“MYHEAD_H已经定义过”这样一种状态。将该符号配合条件编译指令一起使用,可以起到一些特殊作用,这是C++程序中#define
的最常用之处。
#undef
的作用是删除由#define
定义的宏,使之不再起作用。
3. 条件编译指令
使用条件编译指令,可以限定程序中的某些内容要在满足一定条件的情况下才参与编译。因此,利用条件编译指令可以使同一个源程序在不同的编译条件下产生不同的目标代码。例如,可以在调试程序时增加一些调试语句,以达到跟踪目的,并利用条件编译指令,限定当程序调试好以后,重新编译时,使调试语句不参与编译。常用的条件编译语句有:
(1)形式1:
#if 常量表达式
程序段 //当常量表达式为非0时编译本程序段
#endif
(2)形式2:
#if 常量表达式
程序段1 //当常量表达式为非0时编译本程序段
#else
程序段2 //当常量表达式为0时编译本程序段
#endif
(3)形式3:
#if 常量表达式1
程序段1 //当常量表达式1为非0时编译本程序段
#elif 常量表达式2
程序段2 //当常量表达式1为0、常量表达式2为非0时编译本程序段
.
.
.
#elif 常量表达式n
程序段n //当常量表达式1...常量表达式n-1均为0
//常量表达式n不为0时编译本程序段
#else
程序段n+1 //其他情况下编译本程序段
#endif
(4)形式4:
#ifdef 标识符
程序段1
#else
程序段2
#endif
如果“标识符”经#define
定义过,且未经#undef
删除,则编译程序段1,否则编译程序段2。如果没有程序段2,则#else
可以省略:
#ifdef 标识符
程序段1
#endif
(5)形式5:
#ifndef 标识符
程序段1
#else
程序段2
#endif
如果“标识符”未被定义,则编译程序段1,否则编译程序段2。如果没有程序段2,则#else
可以省略:
#ifndef 标识符
程序段1
#endif
4. defined操作符
defined是一个预处理操作符,而不是指令,因此不要以#开头。defined操作符使用的形式为:
defined(标识符)
若“标识符”在此前经#define
定义过,并且未经#undef
删除,则上述表达式为非0,否则上述表达式的值为0。
下面两种写法完全等效:
#ifndef MYHEAD_H
#define MYHEAD_H
...
#endif
等价于:
#if!defined(MYHEAD_H)
#define MYHEAD_H
...
#endif
由于文件包含指令可以嵌套使用,在设计程序时要避免多次重复包含同一个头文件,否则会引起变量及类的重定义。例如,某个工程包括如下4个源文件:
main.cpp
:
#include"file1.h"
#include"file2.h"
int main()
{
...
}
file1.h
:
#include"head.h"
...
file2.h
:
#include"head.h"
...
head.h
:
class Point
{
};
这时,由于#include指令的嵌套使用,使得头文件head.h
被包含了两次,于是编译时系统会出错:Point类被重复定义。如何避免这种情况呢?
这就是要在可能被重复包含的头文件中使用条件编译指令。用一个唯一的标识符来标记某文件是否已参加过编译,如果已参加过编译,则说明该程序段是被重复包含的,编译时忽略重复部分。将文件head.h
改写如下:
#ifndef HEAD_H
#define HEAD_H
class Point
{
};
#endif
在这个头文件中,首先判断标识符HEAD_H是否被定义过。若未定义过,说明此头文件尚未参加过编译,于是编译下面的程序段,并且对标识符HEAD_H进行宏定义,标记此文件已参加过编译。若标识符HEAD_H被定义过,说明此头文件参加过编译,于是编译器忽略下面的程序段。这样就不会造成对Point的重复定义。