Version 15.04
ヘッダーファイルは必要?
「前回は、ヘッダーファイルはただの置き換え、ってことを説明しました」
『まさかソースファイル代わりになるとは……ん? でもそしたら、
ソースファイルとヘッダーファイルの違いって? ただの置き換えなら、
どっちかだけでもいいんじゃない?』
「確かにそうだけど、実際はふたつに分けた方が楽だから分けてあるんで
す」
『分けた方が楽?』
「まずルールとして、基本的にヘッダーファイルはコンパイルできませ
ん」
『え、そうなの?』
「 VC++ が〈コンパイルするのは、拡張子が .c か .cpp のみ〉って決めて
るから。あ、あと .cxx も大丈夫かな」
『ってことは、 .h のヘッダーファイルは』
「コンパイルされません。だから、プログラムを作るためには Main.cpp と
かのソースファイルが必須。このファイルがないとコンパイルされないから
ね」
『ソースファイルは必須、と……じゃあヘッダーファイルがどうかってこと
よね』
「そう。理論上は、ヘッダーファイルは必要ないんだけど、あった方が便利
です。まず、 Sub.cpp と Sub.h に話を絞って考えてみます。 Main.cpp も
含めて、3ファイルがこういう状態の場合」
// Main.cpp
#include <Windows.h>
#include "Sub.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test();
return 0;
}
// Sub.cpp
#include <Windows.h>
void Test()
{
MessageBox( NULL, "サブ", "Sub()", MB_OK );
}
// Sub.h
void Test();
『うん、普通に動くね』
「次に、 Sub.cpp に Test2() という関数を追加して、 Test() から呼び出
す場合を考えます」
// Sub.cpp
#include <Windows.h>
void Test()
{
Test2();
}
void Test2()
{
MessageBox( NULL, "テスト2", "Test2()", MB_OK );
}
『うん、 Test() から Test2() を呼んでるね』
「でも、これだとコンパイルエラーになります」
Sub.cpp(6) : error C2065:
'Test2' : 定義されていない識別子です。
Sub.cpp(10) : error C2373:
'Test2' : 再定義されています。異なる型修飾子です。
『ホントだ。……あ、もしかして Test2() が Test() の後にあるから?』
「そう! 関数を使うためには、その関数の定義か宣言が呼び出す前にない
とだめなんです」
『定義か宣言、どっちかでいいんだ。ってことは』
// Sub.cpp
#include <Windows.h>
void Test2()
{
MessageBox( NULL, "テスト2", "Test2()", MB_OK );
}
void Test()
{
Test2();
}
『うん、これならコンパイル通った。あと、宣言でもいいんだから……』
// Sub.cpp
#include <Windows.h>
void Test2();
void Test()
{
Test2();
}
void Test2()
{
MessageBox( NULL, "テスト2", "Test2()", MB_OK );
}
『うん、これもOK!!』
「あともう1パターン!」
『え……あ、そっか、ヘッダーファイル!』
// Sub.cpp
#include <Windows.h>
#include "Sub.h"
void Test()
{
Test2();
}
void Test2()
{
MessageBox( NULL, "テスト2", "Test2()", MB_OK );
}
// Sub.h
void Test();
void Test2();
『これでもOKだね』
「さて、ヘッダーファイルを使うと便利、つまり一番最後の方法が一番便利
ってことを説明します。まず、最初の方法、 Test2() を Test() の前に
持ってくる方法」
『これは面倒そう……呼び出す関数を必ず上に持っていかなきゃいけないん
だから』
「そういうこと。特に関数同士が複雑に呼び合っている場合には、パズルに
なっちゃいます。なので、そういう〈順番〉を気にしなくていい、宣言を先
に書いておく方法が必須になります」
『で、それをヘッダーファイルにまとめる理由ね。うーん……』
「もし Sub.cpp だけなら、2番目の方法でも問題ないと思うよ」
『 Sub.cpp だけなら……ってことは、他のソースファイルから呼び出す場
合のため、ってこと?』
「そういうこと。コンパイルってソースファイル単位ですることを思い出し
て」
『うん、一番手前のソースファイルだけコンパイルしてた』
「ってことは、ヘッダーファイルのインクルードをして、関数の宣言を憶え
ておく、っていうのもソースファイル単位ってこと」
『???』
「たとえば、 Sub.cpp をコンパイルしたとします。そうすると、 Test()
と Test2() の宣言が出てきてるよね」
『うん』
「次に、 Main.cpp がこうなってた場合」
// Main.cpp
#include <Windows.h>
// #include "Sub.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test();
return 0;
}
『あ、インクルードがコメントアウトされてる……』
「この状態だとコンパイルエラーになります。 Sub.cpp をコンパイルして
続けて Main.cpp をコンパイルしても、 Sub.cpp をコンパイルしたときの
情報、つまり Test() と Test2() の宣言がある、っていう情報はなくなっ
てるから」
『 Test() なんて関数は存在しない、と』
「だから、〈 Sub.cpp の関数を呼び出すソースファイルは、全て Sub.cpp
の関数の宣言を持たなきゃいけない〉ことになります」
『宣言を持つ……』
// Main.cpp
#include <Windows.h>
void Test();
void Test2();
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test();
return 0;
}
『ってすればいいってことよね。でもめんどくさ……あ! そのための
ヘッダーファイル!』
「そういうこと。インクルードすればいいわけでしょ。ソースファイルにあ
る全関数の宣言をヘッダーファイルに書いておけば」
『そのヘッダーファイルをインクルードすれば使えちゃう!』
「そういうこと」
/*
Preview Next Story!
*/
『ヘッダーファイルってそういう便利さがあったのね』
「と言っておいてなんだけど、実は面倒な部分もかなりあるんです」
『げ!』
「たとえばインクルードする順番とか」
『順番? そんな気にしなくてよさそうだけど』
「というわけで次回」
< Version 15.05 インクルードの順番 >
『につづく!』
「ほんっとうに悩まされるんだ、これが……」
『経験者は語る、ね……』