微信公众号: 星点课堂
新浪微博:女儿叫老白
网易云课堂:女儿叫老白
网易云课堂免费课程:《C++跨平台开发中的编译错误》
----------------------------------------------------------------------------
(4.7.2)
引言:
----------------------------------------------------------------------------
我们在编程的时候,经常见到头文件的开头有一个#ifdef的宏定义判断,它是用来干啥的呢?有什么需要注意的呢?今天我们就来讨论一下这个问题。
正文:
----------------------------------------------------------------------------
C++语言允许重声明函数,但不允许重声明结构,因为如果出现两个同名的结构,编译器不知道该用哪一个。而我们的结构体一般放在头文件中,而且头文件很可能被多个cpp包含,当编译某个编译单元(cpp)时,非常有可能发生这种情况:对某一个头文件多次包含(因为不知道再什么地方可能包含该头文件)。比如:
// a.cpp
#include “b.h”
#include “c.h”
// c.h
#include “b.h”
上述代码中,a.cpp包含了b.h和c.h,而c.h又包含了b.h,因此,在编译a.cpp时将会包含两次b.h。而这将导致头文件重入问题,也就是头文件中的结构体或者类会被编译器多次检测到,进而导致编译错误。
预防的方法就是使用预处理指示符:#ifdef, #ifndef, #define #endif等。
用法如下:
// myclass.h
#ifndef __MYCLASS_H__
#define __MYCLASS_H__
…… // 代码区
#endif // __MYCLASS_H__
上述代码是头文件的标准写法。开头先用#ifndef判断是否已经定义了__MYCLASS_H__这个宏,如果没有定义则定义这个宏,如果已经定义了,则该头文件中的所有内容都被编译器跳过。与#ifndef配套使用的是末尾的#endif,它用来告诉编译器#ifndef的判断结束。防止#ifndef的作用域未关闭而影响到后续文件的编译。
这里牵连出#define预处理指示符的用法。一般我们用它来定义常量,比如:
#define PI 3.14
这里我们用来定义一个标识符__MYCLASS_H__,目的是让编译器知道有这样一个符号存在。下次编译器再次进入这个头文件时,因为在此之前编译器已经建立过这个符号了,因此这次就会识别出该符号并跳过整个#ifndef与#endif之间的代码段。
说到#define常量的语法,实际上我们不推荐这种方法。而推荐使用static定义文件局部变量的方法来定义常量。方法如下:
// const.h
static double s_dDetectDistince = 3.f;
static double s_Pi = 3.1415926535897932f;
原因有两个:
1. 编译器无法识别宏定义中的语法错误,如果真的存在错误,那么只能到运行期才能发现。这不是我们希望看到的。
2. 如果您的产品使用命名空间,那么它是无法保护宏定义的,但是static变量和枚举是受命名空间保护的。
因此,建议您尽量使用static变量来定义常量。
结语:
----------------------------------------------------------------------------
#ifndef预处理指示符可以有效防止头文件的多次声明和重入。使用它是头文件的标准编程方式。希望大家都使用公司强制或推荐的编码规范进行编程,这样可以减少很多不必要的麻烦。