在 C 或 C++ 中多次包含头文件是否有用?
如果从不使用该机制,为什么编译器会担心包含两次文件?如果它真的没用,如果新的编译器确保每个头文件只包含一次,会不会更方便?
编辑:
我知道有标准的做事方式,比如include guards和pragma once,但你为什么还要指定呢?只包含一次文件不应该是编译器的默认行为吗?
在 C 或 C++ 中多次包含头文件是否有用?
如果从不使用该机制,为什么编译器会担心包含两次文件?如果它真的没用,如果新的编译器确保每个头文件只包含一次,会不会更方便?
编辑:
我知道有标准的做事方式,比如include guards和pragma once,但你为什么还要指定呢?只包含一次文件不应该是编译器的默认行为吗?
是的,它在使用预处理器生成代码或执行 Boost.PP 之类的技巧时很有用。
例如,请参阅 X 宏。基本思想是文件包含宏的主体和#define
参数,然后#include
是它。这是一个人为的例子:
宏.xpp
std::cout << MESSAGE;
#undef MESSAGE
文件.cpp:
int main() {
# define MESSAGE "hello world"
# include "macro.xpp"
}
这也允许您#if
在参数上使用和朋友,这是普通宏无法做到的。
是的,多次包含标题可能很有用(尽管它相当不寻常)。典型的例子是<assert.h>
,它的定义asssert
不同,取决于是否NDEBUG
定义。因此,包含它是有意义的,然后有一个(通常是有条件的)NDEBUG 定义,然后再次包含它,(至少可能)具有不同的定义assert
:
根据包含的每个时间
assert
的当前状态重新定义宏1。NDEBUG
<assert.h>
然而,大多数标头都为了幂等而付出了一些努力(即,无论它们被包含的频率如何,都具有相同的效果)。
1 C99,§7.2/1。
一个典型的例子(未经测试) - 要点是它考虑了一个枚举列表,因此它们在一个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>";
}
是的,只包含一次会更方便,这就是您使用 #pragma once 的原因。在 C++ 中:)
编辑:
注意:#pragma once 是不可移植的。您可以使用
#ifndef FILENAME_H
#define FILENAME_H
如果您希望它是可移植的,则在您的头文件顶部。
每当您需要一些不想一次又一次地手动维护的“无聊”代码生成时,都可以使用多重包含。
经典示例是 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
#ifndef _INC_HEADER_
#define _INC_HEADER_
//header code
#endif
HEADER
标题名称在哪里
例如。main_win.h
将会_INC_MAIN_WIN_