ヘッダー ファイル (C++)

変数、関数、クラスなどのプログラム要素の名前は、使用する前に宣言する必要があります。 たとえば、最初に 'x' を宣言せずに x = 42 とだけ書き込むことはできません。

int x; // declaration
x = 42; // use x

宣言は、要素が intdouble関数class、または他の要素であるかどうかをコンパイラに指示します。 さらに、各名前は、使用されるすべての .cpp ファイルで (直接または間接的に) 宣言される必要があります。 プログラムをコンパイルすると、各 .cpp ファイルが個別にコンパイル単位にコンパイルされます。 コンパイラは、他のコンパイル単位で宣言されている名前を知らされません。 つまり、クラスまたは関数またはグローバル変数を定義する場合は、それを使用する追加の .cpp ファイルごとにその宣言を指定する必要があります。 その各宣言は、すべてのファイルでまったく同じである必要があります。 リンカーがすべてのコンパイル単位を 1 つのプログラムにマージしようとすると、わずかな不整合によってエラーまたは意図しない動作が発生します。

C++ では、エラーの可能性を最小限に抑えるために、ヘッダー ファイルを使用して宣言を含めるという規則を採用しています。 ヘッダー ファイルで宣言を作成し、その宣言を必要とするすべての .cpp ファイルまたは他のヘッダー ファイルで #include ディレクティブを使用します。 この #include ディレクティブは、コンパイル前にヘッダー ファイルのコピーを .cpp ファイルに直接挿入します。

Note

Visual Studio 2019 では、ヘッダー ファイルの改善と最終的な置き換えとして、C++20 モジュール機能が導入されました。 詳細については、「C++ のモジュールの概要」を参照してください。

次の例は、クラスを宣言し、それを別のソース ファイルで使用する一般的な方法を示しています。 最初に、ヘッダー ファイル my_class.h を使用します。 クラス定義が含まれていますが、定義が不完全である点に注意してください。メンバー関数 do_something が定義されていません。

// my_class.h
namespace N
{
    class my_class
    {
    public:
        void do_something();
    };

}

次に、実装ファイル (通常は .cpp または同様の拡張子を使用) を作成します。 ファイル my_class.cpp を呼び出し、メンバー宣言の定義を指定します。 この時点の .cpp ファイルに my_class 宣言を挿入するために、"my_class.h" ファイルの #include ディレクティブを追加し、std::cout の宣言をプルするために <iostream> を含めます。 引用符はソース ファイルと同じディレクトリ内のヘッダー ファイルに使用され、標準ライブラリ ヘッダーには角かっこが使用されます。 また、多くの標準ライブラリ ヘッダーには、.h や他のファイル拡張子が含まれていません。

実装ファイルでは、必要に応じて using ステートメントを使用して、"my_class" または "cout" のすべてのメンションを "N::" または "std::" で修飾する必要を回避できます。 ヘッダー ファイルに using ステートメントを含めないでください。

// my_class.cpp
#include "my_class.h" // header in local directory
#include <iostream> // header in standard library

using namespace N;
using namespace std;

void my_class::do_something()
{
    cout << "Doing something!" << endl;
}

これで、別の .cpp ファイルで my_class を使用できます。 コンパイラが宣言をプルできるように、ヘッダー ファイルを #include します。 コンパイラが知る必要があるのは、my_class が do_something() というパブリック メンバー関数を持つということだけです。

// my_program.cpp
#include "my_class.h"

using namespace N;

int main()
{
    my_class mc;
    mc.do_something();
    return 0;
}

コンパイラは、各 .cpp ファイルの .obj ファイルへのコンパイルを完了すると、.obj ファイルをリンカーに渡します。 リンカーがオブジェクト ファイルをマージするときに、my_class のただ 1 つの定義を見つけます。これは my_class.cpp 用に生成された .obj ファイル内にあり、ビルドは成功します。

ガードを含める

通常、ヘッダー ファイルには インクルード ガードつまり #pragma once ディレクティブがあり、1 つの .cpp ファイルに複数回挿入されることを回避します。

// my_class.h
#ifndef MY_CLASS_H // include guard
#define MY_CLASS_H

namespace N
{
    class my_class
    {
    public:
        void do_something();
    };
}

#endif /* MY_CLASS_H */

ヘッダー ファイルに配置するもの

ヘッダー ファイルは複数のファイルに含まれる可能性があるため、同じ名前の複数の定義を生成する可能性のある定義を含めすることはできません。 以下は許可されていないか、非常に不適切な方法と見なされます。

  • 名前空間またはグローバル スコープの組み込み型定義
  • 非インライン関数の定義
  • 非 const 変数の定義
  • 集計定義
  • 名前のない名前空間
  • using ディレクティブ

using ディレクティブを使用すると必ずしもエラーが発生するとは限りませんが、そのヘッダーを直接または間接的に含むすべての .cpp ファイルのスコープに名前空間が含まれるため、問題が発生する可能性があります。

サンプル ヘッダー ファイル

次の例は、ヘッダー ファイルで許可されるさまざまな種類の宣言と定義を示しています。

// sample.h
#pragma once
#include <vector> // #include directive
#include <string>

namespace N  // namespace declaration
{
    inline namespace P
    {
        //...
    }

    enum class colors : short { red, blue, purple, azure };

    const double PI = 3.14;  // const and constexpr definitions
    constexpr int MeaningOfLife{ 42 };
    constexpr int get_meaning()
    {
        static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert
        return MeaningOfLife;
    }
    using vstr = std::vector<int>;  // type alias
    extern double d; // extern variable

#define LOG   // macro definition

#ifdef LOG   // conditional compilation directive
    void print_to_log();
#endif

    class my_class   // regular class definition,
    {                // but no non-inline function definitions

        friend class other_class;
    public:
        void do_something();   // definition in my_class.cpp
        inline void put_value(int i) { vals.push_back(i); } // inline OK

    private:
        vstr vals;
        int i;
    };

    struct RGB
    {
        short r{ 0 };  // member initialization
        short g{ 0 };
        short b{ 0 };
    };

    template <typename T>  // template definition
    class value_store
    {
    public:
        value_store<T>() = default;
        void write_value(T val)
        {
            //... function definition OK in template
        }
    private:
        std::vector<T> vals;
    };

    template <typename T>  // template declaration
    class value_widget;
}