本帖最后由 baizy77 于 2018-10-1 20:51 编辑
版权声明 --------------------------------------------------------------------------------------------------------------------- 作者: 女儿叫老白 (白振勇) 转载请注明出处! --------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------
引言:
---------------------------------------------------------------------------- 我们在编程的时候,经常见到头文件的开头有一个#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预处理指示符的用法。一般我们用它来定义常量,比如:
这里我们用来定义一个标识符__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预处理指示符可以有效防止头文件的多次声明和重入。使用它是头文件的标准编程方式。希望大家都使用公司强制或推荐的编码规范进行编程,这样可以减少很多不必要的麻烦。
《C++编程思想》两卷合订本中文版(4.7.2章节),(美) Bruce Eckel Chuck Allison著
|