C/C++のヘッダファイルは、別になくても… 備考録とエトセトラ
ただし、あると便利だよ、っていう。
(こういうネタ、何番煎じだろう)
結論
私がよく意識する内容ですが。
ヘッダ(.h ファイル)は、こうしましょ。
- 他のソースファイルでも使う関数だけ、ヘッダファイルに書く
- ヘッダに書く関数や構造体の宣言は、必要最低限に留める
実装(.c/.cpp)は、こうしましょ。
- 関数や変数は、外部に公開するもの以外、staticをつける
どゆこと?
CやC++なんかでは、ちょくちょく実装と宣言でファイルを分けましょう、なんてのはよくある話なわけですが。
じゃあ、なんでそういうハナシになんの?
最も原始的なプログラム
そもそも、標準ライブラリも含め、実装をすべて1つのファイルに書いてしまえば、ヘッダファイルなんかいらないのです。
ただし、これを毎回やるとなると、ライブラリ関数含め毎回全部コンパイルしなおす必要があるので、現実的ではないわけで。
同じものを、何度もコンパイルする必要ないよね
そこで、よく使うものをあらかじめコンパイルしておきましょう、ってハナシになってくるわけです。
この、あらかじめコンパイルされたものが、よく「(コンパイル済み)オブジェクト」なんて呼ばれたりして、で、これをひとまとめにしたものが、「ライブラリ」なんて形で配布されたりしてます。
で、このライブラリから必要なものを取り出して、実行可能ファイルにする作業が、「リンク」なんて呼ばれたりしてます。
じゃあ、ヘッダはリンクの為に必要なのか
でもまだ、その結論にはいかないわけで。
別にヘッダファイルに分けなくとも、コンパイルさせる方法なんてものはあります。
少なくとも、C言語については、関数宣言や定義がファイルのどこにもなくても、適当に関数呼び出し書いてもコンパイル自体は通ります。 (警告は出ますけど)
ヘッダ無しで、警告も出さずにコンパイルを通す
簡単なハナシで、各実装ファイルの頭に、使う関数の宣言をかいときゃいいんです。
標準ライブラリの関数であれば、こう。
int printf(const char*, ...);
int main(void)
{
printf("hell world.\n");
return 0;
}
これでめでたく、ヘッダなしでコンパイルできます。
問題点
さて、この方法には問題があります。
- 関数を使う全てのファイルに宣言を書く必要がある
- ライブラリ側の実装を変えたときに、宣言を書いたすべてのファイルを修正しないといけない
たとえば、上記の例で、int printf(const char, ,,,)**を、わけあって**int printf(wchar_t, …)にしたいとしましょう。
このとき、コンパイルされるファイルが複数あって、全部のファイルにこんなのが書いてあるのを想像してください。
まぁ、めんどうくさいですわね。
(grepして置換すりゃあいい、とかいわない)
そこで
おなじものを書くなら、同じ物かいたファイル用意しとけばいいんじゃないの?というハナシになります。
で、ここでよーやくヘッダファイルが出てきます。
名前こそ「ヘッダ」ですけど、便宜上の名前であって、実態はどっちかというとシェアファイルといいますか。
#include <stdio.h>って書けば、コンパイルをする前に中身を展開してくれちゃうわけです。
この、「展開」は、「プリプロセッサ」が担当しています。
問題点
わーい楽できる、ということで、メリットもあるんですけど、デメリットもあります。
その中で最も大きな欠点は、「ヘッダに関連するファイルが多くなると、再コンパイルが必要なファイルが増える」点でしょう。
コメントや空行を増やしただけでも、(環境次第ではありますが)再コンパイルが必要になったりしてきます。
プロジェクトが小規模だったり、ハイスペックマッスィーンを使っていると、デメリットにも思わなくなってきますけど。
で
ヘッダファイルは、依存を少なくしつつ、かつ、なるべくマシンや人の手をなるべき煩わせないように書くのが良いでしょう。
これには色々テクニックがあって、例えば、構造体のポインタを参照するような関数をヘッダに書く場合に、構造体の中身をヘッダに書かない、とか。
これは、他のサイトでも色々言及されているはずなので、その辺見て研究しましょうね。
以上、今週も駄文でした。
blog comments powered by Disqus