防止头文件重复包含的两种方法及区别

1.条件编译语句#ifndef#define#endif
相信很多人都知道第一种方法。

#ifndef _TEST_H_    //——1
#define _TEST_H_    //——2
//加入各种变量,函数声明
#endif              //——3

以上代码:如果没有定义TEST_H变量,就继续往下执行,如果定义了,就直接endif,即不做任何操作。
如果一个文件中包含了两个同样的头文件,那么第一次调用这个头文件的时候,没有定义TEST_H,经过1,到达2,定义TEST_H,进行代码段的执行,到3结束。但是当第二次调用头文件时,已经定义了TEST_H,所以就不会进行下面的代码声明了。这就是条件编译进行防止头文件包含的原理
所以可以用条件编译语句进行防止一个文件中重复包含头文件。
这个方法被广为人知,但是,一般在一个工程中,我们至少要加入main.cpp,function.cpp,function.h三个文件。一个为主函数文件,一个为头文件,用于声明变量,类,类模板或函数等,功能文件用来定义函数。
通常我们在main.cpp和function.cpp都会引入#include “function.h”文件。
在这里我再赘述一下C,C++中文件编译链接的顺序:

第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。

第二个阶段编译、优化阶段,将其翻译成等价的中间代码表示或汇编代码,并执行优化。

第三个阶段是汇编,把汇编语言代码翻译成目标机器指令。

第四个阶段是链接,例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

在编译阶段,每个文件是分别编译的,在编译阶段我们可以通过条件编译的方法去防止头文件重复包含,但是由于宏是在编译之前处理的,
即宏的功能仅仅只能体现在编译阶段,因此#ifndef #define #endif语句只能防止编译阶段的“重复包含”错误。在链接时,由于链接器的作用是将编译器生成的.obj文件链接起来并生成最终可执行文件,因此链接器的作用范围是“文件之间”。不会再去处理宏预处理指令,所以条件编译这个方法只能在编译阶段防止重复包含,如果在头文件中进行了定义,即使用了条件编译语句,在链接的时候也会报重定义错误。
其实只要在头文件进行声明,不进行定义,就不会出错。因为定义不能定义两次,但是声明可以声明多次。

总结:条件编译语句可以防止一个文件中对头文件的重复包含,其实也不是没有用的,如果一个文件a.c中包含了头文件a.h和b.h,但是头文件a.h中也包含了b.h,那么条件编译语句就会发挥作用,经过编译生成的.obj文件中只会一个b.h文件
注意:即使重复包含,预处理器仍将整个头文件读入,即使这个头文件所有内容将被忽略。由于这种处理将减慢编译速度,所以如果可能,应该避免出现多重包含
所以单独使用条件编译语句仅仅防止了重复包含头文件的问题,并没有解决重复定义的问题
条件编译语句还有一个缺点,就是包含了不同的头文件时,如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况。
2.#pragma once语句

//方式一:
#ifndef _SOMEFILE_H_
#define _SOMEFILE_H_
//.......... // 一些声明语句
#endif
//方式二:
#pragma once
//... ... // 一些声明语句

以上就是两种方法的区别。
pragma once由编译器提供保证:
同一个文件不会被编译多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。
带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值