お久しぶりです。Konです。
またお力をお借りしたく、よろしくお願い致します。
PCはWinXP、MicrosoftVisualC++6.0 を使用しています。
MFC で、基本クラスをCFormView にしてプロジェクトを作りました。
プロジェクト全体で使用したい下記のような、構造体があります。
struct INDEXSET{
int xx;
int yy;
} StrIndex[DATABUF_NUM];
二重定義を回避するため、#ifndef を使いました。
//---AAAView.h------------------------
D#define NUM 100
#ifndef _AAAView_H_
#define _AAAView_H_
struct INDEXSET{
int xx;
int yy;
} StrIndex[NUM];
#else
extern struct INDEXSET{
int xx;
int yy;
}StrIndex[NUM];
#endif
//---AAAView.cpp------------
#include AAAView.h
//---BBB.cpp----------------
#include AAAView.h
この状態でコンパイルすると
InterFaceView.obj : error LNK2005: struct INDEXSET * StrIndex (?
StrIndex@@3PAUINDEXSET@@A) はすでに InterFace.obj で定義されています。
とエラーになり解決できずに困っています。
何が問題なのか、おわかりになりませんか?
>#else
>extern struct INDEXSET{
> int xx;
> int yy;
>}StrIndex[NUM];
>#endif
で、またstructを定義しているからではないでしょうか?
単に
#else
extern struct INDEXSET StrIndex[NUM];
#endif
とするとどうでしょうか?
原因は、AAAView.cppからでもBBB.cppからでも、
#elseより前の部分が展開されるから。
なぜならば、それぞれのソースコードで、インクルード文を
展開する時点では、_AAAView_H_が定義されてなかったから。
正しく書くには、
//---AAAView.h------------------------
#define NUM 100
#ifndef _AAAView_H_
#define _AAAView_H_
struct INDEXSET{
int xx;
int yy;
};
extern INDEXSET StrIndex[NUM];
#endif
//---AAAView.cpp------------
#include AAAView.h
INDEXSET StrIndex[NUM]; // <- BBB.cppにだけ書くのも可
//---BBB.cpp----------------
#include AAAView.h
Blueさん、たいちうさん、早速のご回答ありがとうございました。
Blueさん
>単に
>#else
>extern struct INDEXSET StrIndex[NUM];
>#endif
>とするとどうでしょうか?
としても、おなじエラーになりました。
たいちうさんの方法でしたら、エラーは出なくなりました。
これは、ヘッダーファイルのインクルードが
AAAView.cpp と BBB.cpp で同時に進行してしまうからなのでしょうか?
また、
>INDEXSET StrIndex[NUM]; // <- BBB.cppにだけ書くのも可
この、どちらか片方に書けばよいというコトが、
すんなり理解できないのですが・・・???
extern INDEXSET StrIndex[NUM]; の意味は「どこかに StrIndex という配列が有る」
つまり、
別のファイルかもしれないし、同一ファイル内かもしれないけれども、
どこかにあるのでそういう名前を以下で使えるようにする、ということ
// 名前を使えるようにする=宣言
extern を伴わずに INDEXSET StrIndex[NUM]; と書くと、これの意味は
「ここで StrIndex という配列を作る」
ここで作るので、もちろんその名前は以下で使えるようになる
// 名前だけでなく実体を作る=定義
AAAView.cpp でも BBB.cpp でも配列実体を作ってしまうと C2005
実体は1個でなければならない、という規則があるため。
んで、この StrIndex は View の持ち物なのかな?
そうであれば現状でよし、
そうでないなら StrIndex.cpp と StrIndex.h を用意するほうが良い。
> これは、ヘッダーファイルのインクルードが
> AAAView.cpp と BBB.cpp で同時に進行してしまうからなのでしょうか?
コンパイルの単位はソースファイルなので、別のソースファイルに
何がインクルードされているかは関係ない。
> >INDEXSET StrIndex[NUM]; // <- BBB.cppにだけ書くのも可
> この、どちらか片方に書けばよいというコトが、
> すんなり理解できないのですが・・・???
AAAView.cppとBBB.cppの両方でStrIndexを使いたいならば、
ソースファイルをコンパイルする時に、StrIndexの宣言を参照できれば良い。
extern INDEXSET StrIndex[NUM]; がそれ。
実態があっても無くてもコンパイルはできる。
ここまでが、コンパイルの話。実行ファイルの断片を作っている。
次に断片やコンパイラが用意している標準関数のライブラリなどをくっ付けて、
実行ファイルを作る(リンク)。
このときに、externのないINDEXSET StrIndex[NUM]; が、1つもなかったり、
複数のソースコードにあったら、リンクエラー。
この話は、#ifndefを使った二重インクルード防止とは関係のない話。
二重インクルード防止は、1つのソースファイルに同じヘッダーファイルを
複数回インクルードするのを防止するための手段。
tetrapodさん、たいちうさん、ありがとうございました。
やはり、参考書読むより、教えて頂いた方が、理解が早いですね。
助かりました。本当にありがとうございます。
StrIndex は View の持ち物です。
今後、Viewの子ウインドウを作る予定で、
その準備として定義をAAAView.cppからAAAView.hに移植したところ
BBB.cppもAAAView.hを呼んでいてエラーになった。という経緯です。
StrIndex.cpp と StrIndex.h を用意するメリットは、
他のソースから、アクセスしやすいからという理解で良いのでしょうか?
子ウインドウは、多数になる予感がしていますので、
なるべくシンプルにわかりやすく管理したいのです。
(話題が変りそうなので、別スレッドにした方が良いですか?)
コンパイルエラーとリンクエラーの違いがいまいちピンと来なかったのですが
たいちうさんの説明でよく分かりました。
> StrIndex.cpp と StrIndex.h を用意するメリットは、
> 他のソースから、アクセスしやすいからという理解で良いのでしょうか?
> 子ウインドウは、多数になる予感がしていますので、
> なるべくシンプルにわかりやすく管理したいのです。
> (話題が変りそうなので、別スレッドにした方が良いですか?)
メリットは、モジュール性が高まることでしょう。
Viewとか、Docとかの他のモジュールとは別の独立したモジュールならば、
AAAView.hやAAAView.cppに間借りするよりも、別のファイルにしたほうが良い。
ですがViewの持ち物ということなので、ファイルを分ける必要はないでしょう。
むしろ、Viewクラスのメンバ変数にすることを検討するべきかと。
>(話題が変りそうなので、別スレッドにした方が良いですか?)
違う話題は、別スレッドが原則です。
長くなりそうなら分けてください。
たいちうさんありがとうございました。
確かに、Viewクラスのメンバ変数にした方が良さそうですね。
当初の目的はクリアしましたので、
ここで解決に致します。
皆様、ありがとうございました。