こんにちは。
ヘッダで変数や関数の実体を定義するのは結構困ったことが起きます。
ヘッダで他で定義された変数や関数を使う旨宣言するのは問題ありません。
この辺、宣言とか定義とか用語が分かりにくいので、「実体定義」と「使用宣言」で使い分けてみます。
(規格書の使い方とたぶん一致していないですが...)
#実体定義:
下記はどちらとも実体を定義しています。ヘッダで実体を定義するのはやめるべきです。
C++
1int foo;
2void bar()
3{
4 :
5}
#使用宣言:
下記はどちらも使うことを宣言しています。ヘッダで行う主な作業の1つです。
C++
1extern int foo;
2void 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から幾度もインクルードされないようにする仕組みです。
例えば、<iostream>
は結構あちこちからインクルードすると思います。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行も使うような場合は、別の構造体に分割した方が良いと思います。
例えば下記のような手法を使えば、自然に分割できるケースが多いです。
C++
1struct SubStructA
2{
3 :
4};
5struct SubStructB
6{
7 :
8};
9struct SubStructC
10{
11 :
12};
13
14struct BigStruct
15{
16 SubStructA mSubStructA;
17 SubStructB mSubStructB;
18 SubStructC mSubStructC;
19};
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/30 12:25