クラス(C++)は、定義をヘッダーファイル(C++)に置き、メンバ関数の本体の一部または全部をソースファイル(C++)に分けることが多い。

これは、ほかのファイルからそのクラスを使うには、クラスの完全な定義が必要になることが多いためである。

基本形

たとえば Monster クラスを分割すると、次のような形になる。

// Monster.h
class Monster
{
public:
    Monster(int health);
    int getHealth() const;
 
private:
    int health_ {};
};
// Monster.cpp
#include "Monster.h"
 
Monster::Monster(int health)
    : health_ { health }
{
}
 
int Monster::getHealth() const
{
    return health_;
}

クラスを使う側は Monster.h をインクルードし、実装は Monster.cpp に置く。

なぜ定義をヘッダに置くのか

クラス型のオブジェクト(C++)を作る、メンバへアクセスする、メンバ関数を呼ぶ、といった場面では、コンパイラがその型の内容を知っている必要がある。 そのため、クラスの定義そのものは通常ヘッダに置かれる。

一方、クラス外で書くメンバ関数定義まで毎回ヘッダに置く必要はない。 本体をソースファイルへ分けることで、実装の見通しをよくしやすい。

前方宣言で足りる場合

前方宣言(C++)だけでよいのは、その型の大きさやメンバ構成を知らなくてもよい場面である。

たとえば、ポインタや参照の宣言なら前方宣言で足りることがある。

class Monster;
 
void printMonster(const Monster& monster);

しかし、Monster monster; のようにオブジェクトそのものを作るには、完全な定義が必要である。

メンバ関数定義をソースファイルへ分ける理由

  • ヘッダを短く保ちやすい
  • 実装とインターフェースを分けやすい
  • ヘッダ変更による再コンパイル範囲を広げにくい

クラス内で定義したメンバ関数は暗黙に inline になる。 一方、クラス外定義したメンバ関数は自動では inline にならないため、通常は1つのソースファイルにまとめて定義する。

補足

  • 小さなアクセス関数などは、クラス定義内にそのまま書かれることも多い
  • テンプレートクラスでは、通常のクラス以上に定義をヘッダに置くことが多い
  • 単一定義規則(C++)に反しないよう、ヘッダに置く定義には注意が必要である

関連

参考