包括警卫
其他头文件可以包括头文件。因此,包含多个标头的源文件(编译单元)可以间接地多次包含一些标头。如果多次包含的头文件包含定义,则编译器(在预处理之后)检测到违反一个定义规则(例如 2003 C++标准的§3.2),因此发出诊断和编译失败。
使用包含警卫可防止多次包含,有时也称为标题保护或宏保护。这些是使用预处理器 #define
,#ifndef
,#endif
指令实现的。
例如
// Foo.h
#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
class Foo // a class definition
{
};
#endif
使用包含警卫的关键优势在于它们可以与所有符合标准的编译器和预处理器一起使用。
但是,包含防护也会给开发人员带来一些问题,因为必须确保宏在项目中使用的所有头中都是唯一的。具体来说,如果两个(或更多)标头使用 FOO_H_INCLUDED
作为其包含保护,则编译单元中包含的第一个标头将有效地防止包含其他标头。如果项目使用许多第三方库以及碰巧使用的头文件,则会引入特定的挑战。
还必须确保包含保护中使用的宏不与头文件中定义的任何其他宏冲突。
大多数 C++实现还支持 #pragma once
指令,该指令确保文件仅在单个编译中包含一次。这是事实上的标准指令,但它不是任何 ISO C++标准的一部分。例如:
// Foo.h
#pragma once
class Foo
{
};
虽然 #pragma once
避免了与包含守卫相关的一些问题,但是标准中的定义 - 本质上是编译器特定的钩子,并且将被不支持它的编译器默默地忽略。使用 #pragma once
的项目更难以移植到不支持它的编译器。
C++的许多编码指南和保证标准特别禁止使用预处理器而不是 #include
头文件或用于在头文件中放置包含保护的目的。