先看一个例子,假设有三个文件:headerA.h、headerB.h、main.cpp,其内容分别如下:
// file: headerA.h struct foo { int member; }; // file: headerB.h #include "headerA.h" // file: main.cpp #include "headerA.h" #include "headerB.h" int main() { return 0; }
其中main.cpp中直接包含了headerA.h头文件,而headerB.h中又再次包含了headerA.h,这使得预处理器在处理main.cpp时添加了两份struct foo的定义,违反了One Definition Rule规则,g++会报出“error: redefinition of 'struct foo'”这样的错误。
【说明】:C++03标准定义了One Definition Rule(ODR)规则,它要求:在任何一个编译单元(translation unit)中,模板、类型、函数和对象可以有多次声明,但只能定义一次。
解决方法是使用#include guards(也称为macro guards),即在头文件中定义一个唯一标识该头文件的宏,然后通过判断该宏是否被定义来决定是否编译其中内容,于是上述例子修改如下:
// file: headerA.h #ifndef __HEADER_A_H__ #define __HEADER_A_H__ struct foo { int member; }; #endif // file: headerB.h #include "headerA.h" // file: main.cpp #include "headerA.h" #include "headerB.h" int main() { return 0; }
在每一个头文件中添加预处理指令来避免头文件内容重复被包含,这也是我们最常见的处理方式。但是有一个问题:其中标识头文件的宏必须唯一。当程序使用第三方库时,这一点很难完全保证,因此,目前的很多编译器(包括gcc、clang、vc、intel c++ complier等)提供了预处理指令#pragma once来解决这个问题,于是上述代码还可修改为:
// file: headerA.h #pragma once struct foo { int member; }; // file: headerB.h #include "headerA.h" // file: main.cpp #include "headerA.h" #include "headerB.h" int main() { return 0; }
【说明】:#pragma once并不是C++标准规定的,但是很多编译器都支持它。In the C and C++ programming languages, #pragma once is a non-standard but widely supported preprocessor directive designed to cause the current source file to be included only once in a single compilation. Thus, #pragma once serves the same purpose as #include guards, but with several advantages, including: less code, avoiding name clashes, and sometimes improved compile speed.