不止一次在C/C++中包含头文件

mat*_*ots 48 c c++ header-files

在C或C++中多次包含头文件是否有用?

如果从未使用过该机制,为什么编译器会担心包含两次文件; 如果真的没用,如果新的编译器确保每个标题只包含一次,那会不会更方便?

编辑:

我知道有一些标准的方法可以做一次包括警卫和编译指示在内的事情,但为什么你必须指定甚至呢?不应该只是编译器的默认行为只包含一次文件吗?

Pub*_*bby 30

是的,在使用预处理器生成代码或者像Boost.PP这样的技巧时,它很有用.

有关示例,请参阅X宏.基本的想法是文件包含宏的主体和你#define的参数然后#include它.这是一个人为的例子:

macro.xpp

std::cout << MESSAGE;
#undef MESSAGE
Run Code Online (Sandbox Code Playgroud)

file.cpp:

int main() {
# define MESSAGE "hello world"
# include "macro.xpp"
}
Run Code Online (Sandbox Code Playgroud)

这也允许你#if在参数上使用和朋友,这是普通宏不能做的事情.


Jer*_*fin 28

是的,包括一个标题不止一次可能是有用的(虽然它是相当不寻常的).规范示例是根据是否<assert.h>定义而asssert不同地NDEBUG定义的.因此,包含它是有意义的,然后有一个(通常是条件性的)NDEBUG定义,然后再次包含它,(至少可能)不同的定义assert:

所述assert宏是根据当前的状态重新定义NDEBUG每个时间 <assert.h>包含1.

然而,大多数标题都会出于某种意义上的幂等(即,无论它们包含多少次都具有相同的效果).


1 C99,§7.2/ 1.


Ton*_*roy 14

一个典型的例子(未经测试) - 重点在于它会对枚举列表进行因子分析,因此它们enum在流式代码中一致地出现:

// states.h
X(Uninitialised)
X(Initialised)
X(Running)
X(Complete)
X(Shutdown)

// app.c++
#if defined(X)
# error X is already defined
#endif

enum States {
    #define X(TAG) TAG,
    #include "states.h"
    #undef X
    Extra_So_Trailing_Comma_Ignored
};

std::ostream& operator<<(std::ostream& os, const States& state)
{
    #define X(TAG) if (state == TAG) return os << #TAG;
    #include "states.h"
    #undef X
    return os << "<Invalid-State>";
}
Run Code Online (Sandbox Code Playgroud)

  • +1有趣的例子. (2认同)

Tho*_*sen 5

是的,只包含一次会更方便,这就是您使用 #pragma Once 的原因。在 C++ 中:)

编辑:

注意:#pragma Once 是不可移植的。您可以使用

#ifndef FILENAME_H
#define FILENAME_H
Run Code Online (Sandbox Code Playgroud)

如果您希望它是可移植的,则应放在头文件的顶部。

  • /我从来不明白为什么人们会选择不可移植的解决方案,而一些“ifdef”并不难。 (13认同)
  • 下划线大写为保留。只需从您的项目名称开始即可。 (5认同)
  • /me 也从来不理解在包含防护中强制使用起始下划线;) (4认同)
  • @Als:我知道它受到较新的 MSVC、GCC 和 clang 编译器的支持,但它仍然不是标准 C++,并且包含防护并不难。至于“#pragma Once”更有效,我很想看到一些比较,因为至少 GCC 和 clang 认识到包含守卫并将它们与“#pragma”非常相似。 (3认同)
  • 在 **C++** 中,您使用标头防护。在不可移植的 C++ 中,您使用 #pragma 一次 (2认同)
  • @honk:使用“#pragma Once”比**标头保护**更有效,并且 pragma Once 是不可移植的并不会造成任何伤害,因为如果编译器不支持它,那么标准要求编译器应该只是忽略它。 (2认同)
  • @Als:但是编译器有可能识别它并做一些意想不到的事情(例如,gcc的真正旧版本“识别”*所有*编译指示 - 但*大多数*产生了许多人认为不受欢迎的效果)。 (2认同)
  • @JerryCoffin:据我了解,在这种情况下,如果它说了什么并做了其他事情,那么它只是一个特定的编译器错误,就像任何其他编译器错误一样。它不应该被视为针对 prama 的可移植性问题吗? (2认同)