48

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

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

编辑:

我知道有标准的做事方式,比如include guardspragma once,但你为什么还要指定呢?只包含一次文件不应该是编译器的默认行为吗?

4

6 回答 6

30

是的,它在使用预处理器生成代码或执行 Boost.PP 之类的技巧时很有用。

例如,请参阅 X 宏。基本思想是文件包含宏的主体和#define参数,然后#include是它。这是一个人为的例子:

宏.xpp

std::cout << MESSAGE;
#undef MESSAGE

文件.cpp:

int main() {
# define MESSAGE "hello world"
# include "macro.xpp"
}

这也允许您#if在参数上使用和朋友,这是普通宏无法做到的。

于 2012-06-04T07:08:52.883 回答
28

是的,多次包含标题可能很有用(尽管它相当不寻常)。典型的例子是<assert.h>,它的定义asssert不同,取决于是否NDEBUG定义。因此,包含它是有意义的,然后有一个(通常是有条件的)NDEBUG 定义,然后再次包含它,(至少可能)具有不同的定义assert

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

然而,大多数标头都为了幂等而付出了一些努力(即,无论它们被包含的频率如何,都具有相同的效果)。


1 C99,§7.2/1。

于 2012-06-04T07:09:48.227 回答
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>";
}
于 2012-06-04T07:46:12.993 回答
4

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

编辑:

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

#ifndef FILENAME_H
#define FILENAME_H

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

于 2012-06-04T07:06:11.160 回答
1

每当您需要一些不想一次又一次地手动维护的“无聊”代码生成时,都可以使用多重包含。

经典示例是 C/C++ 枚举和相应的字符串,它们或多或少看起来像这样:

// MYENUM_VALUES.H
MYENUMVALUE(a)
MYENUMVALUE(b)
MYENUMVALUE(c)
MYENUMVALUE(d)

// MYENUM.H
#define MYENUMVALUE(x) x,
enum MyEnum
{
#include "MYENUM_VALUES.H"
}
#undef MYENUMVALUE

// MYENUMTOSTRING.C
#define MYENUMVALUE(x) case x : return #x;

const char * MyEnumToString(MyEnum val)
{
    switch (val)
    {
    #include "MYENUM_VALUES.H"
    default return "unknown";
    }
} 
#undef MYENUMVALUE
于 2012-06-06T11:36:37.517 回答
-6
#ifndef _INC_HEADER_
#define _INC_HEADER_

//header code

#endif

HEADER标题名称在哪里

例如。main_win.h将会_INC_MAIN_WIN_

于 2012-06-04T07:09:17.120 回答