Version 15.05
インクルードの順番
「前回は、ヘッダーファイルがなぜ便利なのか説明しました」
『よーするに関数を呼ぶにはその宣言が必要、その宣言だけをまとめた
ヘッダーファイルを使い回せばいろんなソースファイルから関数が呼べる、
ってことよね』
「そういうこと。そんなヘッダーファイルについて、細かいテクニックや、
気を付けておかなければならない点について説明します」
『はーい』
「まずはこれから」
・2種類のインクルード方法について
#include <Windows.h>
#include "Sub.h"
『あ、 <> で囲むのと "" で囲むのと2種類あるんだよね』
「そう。 <> は API や MFC のヘッダーファイルをインクルードする時に、
"" はプロジェクト内のヘッダーファイルをインクルードするときに使いま
す」
『 API の?』
「そう。具体的には……メニューの【ツール】−【オプション】の
【ディレクトリ】ページで、【表示するディレクトリ】を
【インクルードファイル】を選んでみて」
『 C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE とかある』
「これは、 Visual C++ をインストールした時に、 API や MFC の
ヘッダーファイルをインストールしたフォルダ。で、実は <> で
インクルードするのは、ここに書かれているフォルダの中の
ヘッダーファイルなんです」
『そっか、 API とかもちゃんとヘッダーファイルがあって、ここに置かれ
てて、 <> だとここを見に行って……』
「だから、逆もOK」
『逆?』
「たとえばここから Windows.h をプロジェクトへコピーして "Windows.h"
でインクルードしたり」
『できるんだ……』
「プロジェクトのヘッダーファイルを置いてあるフォルダをここの設定に追
加して <Sub.h> ってインクルードしたり」
『それもできるんだ……』
「特に、自分でライブラリを作って使う場合には、ヘッダーファイルの置い
てあるフォルダをここで指定すれば」
『ライブラリっぽく使える! なんかちょっとかっこいいかも』
「では次」
・インクルードの順番
「前回、インクルードは〈コピペするようなもの〉って説明しました」
『うん、ただファイルの中身をそのまま置き換えるだけなんだよね』
「だから、インクルードする順番が実は重要なんです」
『順番?』
「プログラムのコンパイルって、上からするって説明したでしょ」
『うん。上から見ていって、関数の宣言があったら憶えておく、とか』
「だから、こういう場合に問題になります。まず Typedef.h ってファイル
を追加して」
『ほい。【ファイル】−【新規作成】で【ファイル】の
【C/C++ ヘッダーファイル】、ファイル名は Typedef.h ね』
「その中にこう書いて」
// Typedef.h
typedef int INTEGER;
『 typedef って、型の別の名前付けるのだよね』
「そう、 Version 5.08 ( No.073 ) でやったね。次に Sub.cpp をこうしま
す」
// Sub.cpp
#include "Typedef.h"
#include "Sub.h"
void Test( INTEGER p_i )
{
}
「さらにそのヘッダーファイル Sub.h をこうします」
// Sub.h
void Test( INTEGER p_i );
『 Typedef.h の INTEGER を引数に使ってるわけね』
「で、最後にこれを使う Main.cpp 」
// Main.cpp
#include <Windows.h>
#include "Typedef.h"
#include "Sub.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test( 100 );
return 0;
}
『ビルドっと。あれ、普通に通ったけど』
「そう、これは〈正解〉」
『じゃあ間違った例は?』
「さっきも言ったように、インクルードファイルの順番を間違えた場合」
// Main.cpp
#include <Windows.h>
#include "Sub.h"
#include "Typedef.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test( 100 );
return 0;
}
『あ、 Sub.h と Typedef.h のインクルードの順番が逆だ』
「そうするとこういうコンパイルエラーが出ます」
error C2065: 'INTEGER' : 定義されていない識別子です。
「これは、インクルードファイルをそのまま展開してみればわかると思いま
す」
void Test( INTEGER p_i );
typedef int INTEGER;
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
Test( 100 );
return 0;
}
『あ、 typedef する前に Test() の引数で使ってる!!』
「そう、上から順番に見ていくから、 Test() の引数で使う前に typedef
してなきゃいけない、だけどしてないからコンパイルエラーになるんです」
『順番が大事ってこういうことなんだ』
「これは自分で作ったヘッダーファイルだから分かりやすいけど、 API や
MFC のヘッダーファイルでもこういうことはあるから注意して」
『……ってゆーか、それってもしかしてヘッダーファイルの中身見なきゃ、
どっちを先にとかって分からなくない?』
「うん、わからないかも」
『げ』
「逆に言うと、ヘッダーファイルは見ればなんとかなるって言うことも言え
るけどね」
『それはそうだけど……』
「順番に関して、どうしようもない場合とかもあります」
『どうしようもない場合?』
・ヘッダーファイル同士の相互参照
「まず Sub.h が……」
// Sub.h
void Test( INTEGER p_i );
typedef int INTEGER2;
『もうひとつ int が再定義』
「そして Typedef.h が……」
// Typedef.h
typedef int INTEGER;
void Test2( INTEGER2 p_i );
『こっちでその INTEGER2 を使ってる……?』
「どっちを先にインクルードすればいいと思う?」
『 Typedef.h の Test2() に Sub.h の INTEGER2 があるから、 Sub.h を先
に……したら、さっきと同じことになっちゃうから、 Typedef.h を先に……
でもそしたら……これってダメじゃん!!』
「そういうこと。こういうヘッダーファイルを作ると、どちらを先に
インクルードしてもコンパイルエラーになります」
『……じゃあ、そういう場合、どうすればいいの?』
「このヘッダーファイルを自分で作った場合には、整理すればいいだけ。
INTEGER2 を Typedef.h に、 Test2() を Sub.h に置けば問題ないでしょ」
『そっか、 typedef は Typedef.h に、関数は Sub.h に……』
「最悪、自分が作ったヘッダーファイルじゃないとしても、全部コピペして
新しいヘッダーファイルを自分で作っちゃえばいいだけ」
『へ? それっていいの?』
「もちろん。ヘッダーファイルは、単にプログラムの〈情報〉があるだけ。
だから使いづらかったら自分で作り直しちゃえばいいんです」
『それって結構すごいかも……』
「ま、最終手段だけどね」
/*
Preview Next Story!
*/
『ヘッダーファイルを作るって、なんかすごすぎるんだけど』
「ヘッダーファイルは古い技術の集まりだからね」
『古いの?』
「古いよ、だから手作業的な部分が多いし、小さなテクニックも」
『テクニック?』
「というわけで次回」
< Version 15.06 ヘッダーファイルを2度読み込む? >
『につづく!』
「この2度読み込むのを回避する方法とかね」
『あれ? 前に教わってない?』
「ぎく」