時代遅れの技術に頼るのはもうやめよう(やめたい)

 C++を使う上で必須の知識であるにも関わらず、入門書の類には全くと言っていいほど紹介されないTipsに「インクルードガード」があります。これ、正式な呼称では無いので一般には通じないかもしれません*1。呼び名が無い事自体おかしいんだけどな……。
 このインクルードガードは不自然かつ古臭い事この上無いTipsなのですが、何故かいまだにみなさん何の疑問も無く使っているように見えます。なんで?

*1:Bjarne Stroustrup博士[C++の作者]が著書の中でインクルードガードと呼んでいるそうです

一応理屈の説明を

"#"で始まる命令はプリプロセッサ命令と言って、コンパイルの前のタイミングにプリプロセッサによって処理される物です(プリプロセス処理と言います)。

どこかの.cppファイルの内部で[#include"hoge.h"]と書かれていれば、プリプロセッサhoge.hを頭から読んで行きます。

#ifndef HOGE_H

#ifndefは、その後に続くトークンが定義されていなければ、#ifndef以降#endifまで(あるいは#elseまで)のテキストをコンパイルに含めます。トークンの定義というのは#defineで行います。この場合、"HOGE_H"はまだ定義されていないので、#ifndef以降のテキストが読み込まれます。

#define HOGE_H

#ifndefの直後に"HOGE_H"が定義されます。

#endif

ここまで読んで終わり

 さて、別の.cppファイルがやはり[#include"hoge.h"]と書いてあれば、プリプロセッサは馬鹿正直に同じファイルを読みに行きます。しかし、今度は"HOGE_H"が定義されているので、#ifndef内はスルーされます。これによって、ヘッダファイルが重複してインクルードされるのを防ぎます。これが「インクルードガード」です。

インクルードガードの必要性

 なぜ重複インクルードされると困るのかといいますと、コンパイルエラーになるからです(笑)。理由を以下に説明します

 #includeが行う処理は(わかりやすく言えば)テキストファイル(この場合は.cpp)の中に別のテキストファイル(この場合はhoge.h)を単純にコピー&ペーストするだけなのです。これにより、以下のような3つのヘッダファイルを作ったときに問題が発生します。

classA.h

 class classA{
 	...
 };

classB.h

 #include "classA.h"
 class classB{
 	...
 };

classC.h

 #include "classA.h"
 #include "classB.h"
 class classC{
 	...
 };

この時、classC.hは、実際には以下のように展開されてしまいます

 class classA{
 	...
 };
 class classA{ //classB.hでインクルードされた//classC.hでインクルードされた
 	...
 };
 class classB{                               //classC.hでインクルードされた
 	...
 };
 class classC{
 	...
 };

 みてわかるとおり、classAが2回宣言されてしまいます。これは「すでに定義されているクラスが再定義されました」という旨のコンパイルエラーを吐き出します。