为我的 h/hpp 文件创建标头保护一直是我的标准做法,但我想知道,为什么甚至可以将同一文件包含两次?是否存在您实际上需要不受保护的标头的情况?
6 回答
“参数化”头文件可用于在 C 中模拟 C++ 风格的模板。在这种情况下,头文件将取决于许多宏(“模板参数”)。它将根据这些宏的实际“值”生成不同的代码。
因此,此类标头的典型用法是一系列“模板参数”宏定义,然后是#include
指令,然后是另一个“模板参数”宏定义序列,然后是相同的#include
,依此类推。
https://stackoverflow.com/a/7186396/187690
使用此技术时,您将看到没有任何包含保护的头文件或带有仅覆盖文件一部分的包含保护的头文件。
像 Boost.PP 这样的东西通过多次包含标头来完成很多技巧。它本质上允许原始形式的循环。
此外,X-Macros被设计为包含多次。
在 C 中:
#undef NDEBUG
#include <assert.h>
...code using active asserts...
#define NDEBUG
#include <assert.h>
...code using disabled asserts...
冲洗并重复。C++ 中的类似物<cassert>
改为使用标头。
因此,有时有理由两次包含标题。不经常,但有理由这样做。
像这样的情况很少见,当它们确实存在时,无论如何重新设计更适合。我能想到的一个是包含声明的标题:
//functions.h
virtual void foo();
virtual void goo();
//classes.h
class A : public Base
{
#include "functions.h"
};
class B : public Base
{
#include "functions.h"
};
如果包含警卫,这将不起作用functions.h
,但话又说回来,这是非常尴尬的代码......
想象一下,您正在为您的微型引擎编写自己的模板数组包装器(元素是指针),其中数组可以是:
- 动态或静态
- 索引/订单是否重要
- 您可以在数组外部或内部删除一个元素(不仅仅是从数组中删除)
所以有了这个,你可以创建一个类来处理所有这些,但它总是会检查我们能做什么或不能做什么。最好是创建单独的类,但是在这里我们在它们之间有很多相同的代码,你可以(如果不小心的话)要么在其中一个函数中出错,而且添加一个新函数会更慢。
所以最好是利用“参数化”头文件(如AnT所述)并简单地创建类
- Cl_Array_Dy、Cl_Array_DyIn、Cl_Array_DyDel、Cl_Array_DyInDel
- Cl_Array_St、Cl_Array_StIn、Cl_Array_StDel、Cl_Array_StInDel
代码示例:
// TestDfM.h
#ifndef TEST_DFM_H
# define TEST_DFM_H
// first we need to make sure neither of these is defined
# ifdef Df_ARG1
# undef Df_ARG1
# endif
# ifdef Cl_First
# undef Cl_First
# endif
# ifdef Cl_Second
# undef Cl_Second
# endif
# ifdef Df_FIRST
# undef Df_FIRST
# endif
# ifdef Df_SECOND
# undef Df_SECOND
# endif
# ifdef TEST_DF_H
# undef TEST_DF_H
# endif
// we need this
# define Df_FIRST 1
# define Df_SECOND 2
// first class creation
# define Df_CLASS Df_FIRST
# define Df_ARRAY Cl_First
# include "TestDf.h"
// cleanup (after 1st)
# undef Df_CLASS
# undef Df_ARRAY
// second class creation
# define Df_CLASS Df_SECOND
# define Df_ARRAY Cl_Second
# define Df_ARG1
# include "TestDf.h"
// cleanup (after 2st)
# undef Df_CLASS
# undef Df_ARRAY
# undef Df_ARG1
// so we theoretically cannot include TestDf.h anymore (anywhere)
# define TEST_DF_H
#endif // TEST_DFM_H
// TestDf.h
// nothing to do here if the main header for this was not included
// also we should only call this inside the main header
#if defined(TEST_DFM_H) && !defined(TEST_DF_H)
# include "../Includes.h"
class Df_ARRAY {
public:
int m_shared;
# ifndef Df_ARG1
Df_ARRAY(int in_shared=0) { m_shared= in_shared; }
void f_info() { printf("out: %d\n", m_shared); }
# else
int m_x;
Df_ARRAY(int in_shared=0, int in_x= 7) { m_shared= in_shared; m_x= in_x; }
void f_info() { printf("out: %d [also has %d]\n", m_shared, m_x); }
# endif
# if Df_CLASS == Df_FIRST
void f_class() { printf("1st\n"); }
# elif Df_CLASS == Df_SECOND
void f_class() { printf("2nd\n"); }
# endif
};
#endif // TEST_DFM_H
// Main.cpp
#include "Array/TestDfM.h"
int main(int argc, char** argv) {
Cl_First a(6);
Cl_Second b(2);
a.f_class(); // 1st
b.f_class(); // 2nd
a.f_info(); // out: 6
b.f_info(); // out: 2 [also has 7]
return 0; }
头文件只是在遇到时以文本形式包含在内。没有任何真正的理由不能多次包含它们。如果标头仅用于声明而没有定义(并且没有使用默认参数的模板声明),那么多次包含它们甚至没有任何问题。
这<cassert>
就是多次包含文件的典型示例:您可以在一个翻译单元内更改宏的定义NDEBUG
并获得不同的行为。assert()
现在拥有include_once
一个只包含一次文件的东西并不像人们想象的那么简单。这是一个不清楚foo.h
应该多久包含一次的示例:
#include_once "foo.h"
#include_once "./foo.h"
#include_once "bar/foo.h"
假设include_once
每个文件只包含一次,应该多久foo.h
包含一次?所有三个文件都可以很容易地引用同一个物理文件,但这可能不容易看到,例如,因为一个是到另一个的链接。最好将控制权交给程序员如何控制它们最终被使用的频率。