在制作C/C++项目的过程中,应该会遇到关于头文件被重复包含的问题,几乎每一个C/C++程序员都应该知道如何来解决这一问题。通常来说,我们通常可以用两种方式来解决这一问题。
第一种 ---- 利用以下形式:
#ifndef __XX_H__ #ifndef XX_H
#define __XX_H__ 写成这样也可以--> #define XX_H
<头文件定义正文> <头文件定义正文>
#endif
#endif
__XX_H__ 其实就是一个标志,(这个标志本来可以随便自己定义,但是为了防止混乱,所以一般都会采用自己的文件名字:__XX_H__),而且 ifndef 也可以写成 if !defined 效果是一样的。(注:ifndef 不要错写成 ifdef)
当然还有第二种 ---- 在windows平台下,有一个很方便的宏: #pragma once
这个宏我们用MSDN打开查看一下可以得到以下解释内容:
"Specifies that the file will be included (opened) only once by thecompiler when compiling a source code file."
从字面上看这句英文的主要意思就是防止头文件被重复包含。于是我们就想,既然这两种方法都可以起到防止头文件被多次包含的作用,那么这两种方式有什么不同呢,或者说有什么相同之处呢?
首先我们来说一说重复包含的影响:
C/C++在预处理的时候,include相同的文件,预处理器会检查XXX是否有定义再决定要不要复制内容,重复包含会是编译器多检查几次而已。另外在使用增量编译的时候,这个文件变化,所有 include 这个文件的文件都需要重新编译,即使没有去使用里面的任何内容,所以重复包含最经常带来的错误就是重定义。
在用VC6.0向导生成的头文件中,经常可以看见如下的代码段:
#if !defined(AFX_RESIZABLELAYOUT_H__INCLUDED_)
#define AFX_RESIZABLELAYOUT_H__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
...
#endif // !defined(AFX_RESIZABLELAYOUT_H__INCLUDED_)
对于宏有基本了解的朋友应该都知道,头文件中如下的宏定义,是为了避免同样的头文件在同一个.C文件或者.CPP文件多次包含。
#if !defined(XXX)
#define XXX
#endif
由此可知这两种方式都可以很好的起到避免重复包含的作用,但是这两种有什么区别呢?
疑惑就此产生了,既然宏"#if !defined"已经有这个作用了,为何还要一个"#pragma once"呢?
其实虽然"#ifndef"和"#pragma once"都有避免重复包含的功能,但是在实现上还是有区别的。举一例如下:
// Test1.h
#ifndef __COMMENT_CONVERT_H__
#define __COMMENT_CONVERT_H__
...
#endif
// Test2.h
#pragma once
...
// Test.cpp
#include "Test1.h" //
#include "Test1.h" //
#include "Test2.h" //
#include "Test2.h" //
...
头文件Test1.h中用宏来避免重复,头文件Test2.h中用#pragma once来避免重复。编译Test.cpp,将需要打开Test1.h两次,第一次发现宏__COMMENT_CONVERT_H__没有定义,接着就处理宏定义;第二次打 开Test1.h时,发现宏__COMMENT_CONVERT_H__已经定义过了,编译器就会略过宏定义部分,直到处理完Test1.h末的#endif。
而由于头文件Test2.h使用#pragma once来避免重复定义的,在编译Test.cpp的过程中,Test2.h只会被打开一次,也就是处理到第3行的时候。因为Test2.h用的 是#pragma once,所以在处理完第3行后,编译器已经知道包含了一次Test2.h,在它(编译器)处理第4行代码时,发现Test2.h已经包含过了,忽略掉第 4行代码,也就不需要再次打开Test2.h进行判断了。
总结一下,除了#pragma once是微软编译器所特有的之外,用宏和#pragma once的办法来避免重复包含头文件,主要区别在于宏处理的方法会多次打开同一头文件,而#pragma once则不会重复打开,从而#pragma once能够更快速。
用更确切的语言总结一下就是:#pragma once指定当前文件在构建时只被包含(或打开)一次,这样就可以减少构建的时间,因为加入#pragma once后,编译器在打开或读取第一个#include 模块后,就不会再打开或读取随后出现的同#include 模块。