找回密码
 立即注册
收起左侧

C++老鸟日记019 头文件的多次声明与预处理指示符

0
回复
4797
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-9-12 22:52:55 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2018-10-1 20:51 编辑

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
本套课程属于:《C++跨平台开发干货》系列课程。
-----------------------------------------------------------------------------

引言:
----------------------------------------------------------------------------
       我们在编程的时候,经常见到头文件的开头有一个#ifdef的宏定义判断,它是用来干啥的呢?有什么需要注意的呢?今天我们就来讨论一下这个问题。

正文:
----------------------------------------------------------------------------
       C++语言允许重声明函数,但不允许重声明结构,因为如果出现两个同名的结构,编译器不知道该用哪一个。而我们的结构体一般放在头文件中,而且头文件很可能被多个cpp包含,当编译某个编译单元(cpp)时,非常有可能发生这种情况:对某一个头文件多次包含(因为不知道再什么地方可能包含该头文件)。比如:

  1. // a.cpp
  2. #include “b.h”
  3. #include “c.h”

  4. // c.h
  5. #include “b.h”
复制代码


       上述代码中,a.cpp包含了b.h和c.h,而c.h又包含了b.h,因此,在编译a.cpp时将会包含两次b.h。而这将导致头文件重入问题,也就是头文件中的结构体或者类会被编译器多次检测到,进而导致编译错误。
       预防的方法就是使用预处理指示符:#ifdef,#ifndef, #define #endif等。
       用法如下:

  1. // myclass.h
  2. #ifndef  __MYCLASS_H__
  3. #define __MYCLASS_H__
  4. …… // 代码区
  5. #endif // __MYCLASS_H__
复制代码


       上述代码是头文件的标准写法。开头先用#ifndef判断是否已经定义了__MYCLASS_H__这个宏,如果没有定义则定义这个宏,如果已经定义了,则该头文件中的所有内容都被编译器跳过。与#ifndef配套使用的是末尾的#endif,它用来告诉编译器#ifndef的判断结束。防止#ifndef的作用域未关闭而影响到后续文件的编译。
       这里牵连出#define预处理指示符的用法。一般我们用它来定义常量,比如:

  1. #define PI      3.14
复制代码

     这里我们用来定义一个标识符__MYCLASS_H__,目的是让编译器知道有这样一个符号存在。下次编译器再次进入这个头文件时,因为在此之前编译器已经建立过这个符号了,因此这次就会识别出该符号并跳过整个#ifndef与#endif之间的代码段。
       说到#define常量的语法,实际上我们不推荐这种方法。而推荐使用static定义文件局部变量的方法来定义常量。方法如下:

  1. // const.h
  2. static double s_dDetectDistince = 3.f;
  3. static double s_Pi  = 3.1415926535897932f;
复制代码


       原因有两个:
       1.  编译器无法识别宏定义中的语法错误,如果真的存在错误,那么只能到运行期才能发现。这不是我们希望看到的。
       2. 如果您的产品使用命名空间,那么它是无法保护宏定义的,但是static变量和枚举是受命名空间保护的。
       因此,建议您尽量使用static变量来定义常量。

结语:
----------------------------------------------------------------------------
       #ifndef预处理指示符可以有效防止头文件的多次声明和重入。使用它是头文件的标准编程方式。希望大家都使用公司强制或推荐的编码规范进行编程,这样可以减少很多不必要的麻烦。
《C++编程思想》两卷合订本中文版(4.7.2章节),(美) Bruce Eckel  Chuck Allison著
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了