クラス(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++)に反しないよう、ヘッダに置く定義には注意が必要である