h/hpp ファイルのヘッダー ガードを作成することは、私にとって常に標準的な方法ですが、同じファイルを 2 回インクルードできるのはなぜでしょうか? 保護されていないヘッダーが実際に必要な場合はありますか?
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>
代わりにヘッダーを使用します。
そのため、ヘッダーを 2 回含める理由がある場合があります。頻繁ではありませんが、そうする理由があります。
小さなエンジン用に独自のテンプレート配列ラッパー (要素はポインター) を作成していると想像してください。配列は次のいずれかになります。
- 動的または静的
- インデックス/順序が重要かどうか
- 配列の外側または内側の要素を削除できます(配列から削除するだけではありません)
したがって、これを使用して、これがすべて処理される1つのクラスを作成できますが、できることとできないことを常にチェックします。別々のクラスを作成するのが最善ですが、ここではそれらの間に非常に多くの同じコードがあるため、(注意しないと) それらの関数の 1 つを間違えたり、新しい関数を追加すると遅くなる可能性があります。
「パラメータ化された」ヘッダーファイルを使用して(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; }
このようなケースはまれであり、存在する場合でも再設計が適しています。私が考えることができるのは、宣言を収集するヘッダーです。
//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
ガードが含まれていた場合、これは機能しませんが、これはかなり厄介なコードです...
ヘッダー ファイルは、検出されるたびにテキストでインクルードされます。それらを複数回含めることができない本当の理由はありません。ヘッダーが宣言のみに使用され、定義がない場合 (およびデフォルト引数を持つテンプレートの宣言がない場合)、それらを複数回含めても問題はありません。
これ<cassert>
は、ファイルを複数回インクルードする標準的な例です。1 つの翻訳単位内でマクロの定義を変更NDEBUG
し、マクロから異なる動作を得ることができます。assert()
ファイルを一度だけインクルードするようなものを持つことinclude_once
は、人々が考えがちなほど簡単ではないことが判明しました。foo.h
以下は、どのくらいの頻度で含める必要があるかが明確でない例です。
#include_once "foo.h"
#include_once "./foo.h"
#include_once "bar/foo.h"
各ファイルを 1 回だけインクルードすると仮定するとinclude_once
、どのくらいの頻度でインクルードする必要foo.h
がありますか? 3 つのファイルはすべて、同じ物理ファイルを簡単に参照できますが、たとえば、1 つのファイルが別のファイルへのリンクになっているなどの理由で、簡単には表示されない場合があります。最終的に使用される頻度を制御する方法をプログラマーに任せるのが最善のようです。