質問: プログラミング初心者です。 くだらない質問だったらごめんなさい。 環境はWindowsのVisual C++(2015 RC)です。 分割コンパイルのヘッダファイルについてなのですが、インターネットで調べるとヘッダファイルで変数や関数の定義はするな、と書いてありました。ですが、#ifndef-#define-#endif や #pragma once を使えば二重定義は回避できるとも書いてありました。 そこで、新しく作ったファイルにtest.hというヘッダファイルを追加して、そこに#pragma onceと書いたうえで変数の定義をしたところ問題なく実行できてしまったのですが、これは#pragma onceさえ使えばヘッダファイル内で定義をしても問題ないということですか?それとも気づかないところで何か問題が起こっているのでしょうか。 また、構造体の要素数がかなり多くなりそうなので別のファイルに定義したくて分割コンパイルについて調べたので、別のファイルで定義する良い方法が他にありましたら教えていただけるとありがたいです。 よろしくお願いします。
回答: こんにちは。 ヘッダで変数や関数の実体を定義するのは結構困ったことが起きます。 ヘッダで他で定義された変数や関数を使う旨宣言するのは問題ありません。 この辺、宣言とか定義とか用語が分かりにくいので、「実体定義」と「使用宣言」で使い分けてみます。 (規格書の使い方とたぶん一致していないですが...) 実体定義: ===== 下記はどちらとも実体を定義しています。ヘッダで実体を定義するのはやめるべきです。 ``` int foo; void bar() { : } ``` 使用宣言: ===== 下記はどちらも使うことを宣言しています。ヘッダで行う主な作業の1つです。 ``` extern int foo; void bar(); ``` 実体をヘッダで定義すると下記問題が発生します。 ======================= 通常ヘッダは複数のcppファイルからインクルードされます。 例えば、main.cppやsub.cppからfoo.hをインクルードし、2つとものcppを使って1つのexeを作るようなことを良く行います。 この時、foo.hで「実体定義」していると、main.cppとsub.cppの両方でその実体が定義されてしまいます。結果、多重定義エラーになってしまいます。 しかし、例えば、実体定義はmain.cppで行い、foo.hでは使用宣言のみを行えば、多重定義エラーを避けることができるのです。 インクルード・ガード ========== 以上の話は、`#pragma once`の動作とは無関係です。 `#pragma once`は同じヘッダが同じcppから幾度もインクルードされないようにする仕組みです。 例えば、``は結構あちこちからインクルードすると思います。cppから直接インクルードしたり、そのcppがインクルードしているヘッダからインクルードしたりしてしまいます。 すると何度も同じものがインクルードされるため、例えば`#define FOO xxx`などの定義が多重定義になってしまいます。それを避けるのが`#pragma once`等のインクルード・ガードです。 1つのcppから一度インクルードしたヘッダを、同じcppからヘッダ等を経由して再度インクルードしても中身をコンパイルしない仕組みです。 ついでにコンパイル単位について補足 ================= Visual Studioのソリューション・エクスプローラのSource Filesに登録した1つ1つのソース・ファイルが事実上「コンパイル単位」となります。 これらのソースを1つ1つコンパイルして、objファイルを作り、最後にリンクしてexeファイルを作ります。 このソースを1つコンパイルすることを指して「コンパイル単位」と呼びます。 1つのコンパイル単位の中で、同じヘッダが複数回インクルードされないようにする仕組みがインクルード・ガードです。 複数のコンパイル単位の中で、同じ実体が複数回定義されないようにするために、ヘッダで実体定義しないようにします。 【追記】 > > また、構造体の要素数がかなり多くなりそうなので別のファイルに定義したくて分割コンパイルについて調べたので、別のファイルで定義する良い方法が他にありましたら教えていただけるとありがたいです。 > > > 1つの構造体を複数のファイルに渡って定義することも不可能ではないですが、あまり行わないです。 たった1つの構造体の定義に例えば1,000行も使うような場合は、別の構造体に分割した方が良いと思います。 例えば下記のような手法を使えば、自然に分割できるケースが多いです。 ``` struct SubStructA { : }; struct SubStructB { : }; struct SubStructC { : }; struct BigStruct { SubStructA mSubStructA; SubStructB mSubStructB; SubStructC mSubStructC; }; ```