web-dev-qa-db-ja.com

複数のcppsに同じヘッダーを含めることによる複数の定義エラーの繰り返し

したがって、私が何をしているように見えても、同じプロジェクトの複数のソースコードファイルに同じヘッダーファイルを含めた結果として、Dev C++が多数の複数定義エラーを吐き出すのを避けられないようです。すべてのソースコードを1つのファイルにダンプする必要はなく、ヘッダーを1回だけ含めることを強くお勧めします。これにより、ファイルが非常に長くなり、管理が困難になるためです。

基本的に、これが起こっていることです:

#ifndef _myheader_h
#define _myheader_h

typedef struct MYSTRUCT{
int blah;
int blah2; } MYSTRUCT;

MYSTRUCT Job_Grunt;
MYSTRUCT *Grunt = &Job_Grunt;
MYSTRUCT Job_Uruk;
MYSTRUCT *Uruk = &Job_Grunt;

int Other_data[100];

void load_jobs();

#endif

Cppファイルの例(ほとんどすべてが次のようになります):

#include "myheader.h"

void load_jobs(){

Grunt->blah = 1;
Grunt->blah2 = 14;

Uruk->blah = 2;
Uruk->blah2 = 15;

return; }

この1つのヘッダーを含む約5つのcppファイルがあり、それぞれがヘッダーファイルにある異なるタイプの構造体を処理していることに注意してください。この例では、実際のヘッダーファイルにさらに多くのメンバーを持つ約4〜6の異なる構造体がある場合、2つのメンバーを含む構造体は1つしかありませんでした。私がそれを含めたすべてのファイルは、この例で見られるのと同じ式に従います。

これで、ヘッダーガードは、個々のcppファイルがヘッダーファイルを複数回インクルードするのを阻止するだけであることを理解しました。コンパイラが各cppの先頭でインクルードを読み取ると、ヘッダーファイルが再度定義されるため、次の行が吐き出されます。

Multiple Definition of Uruk, first defined here  
Multiple Definition of Job_Uruk, first defined here  
Multiple Definition of Grunt, first defined here  
Multiple Definition of Job_Grunt, first defined here  
Multiple Definition of Other_data, first defined here

ヘッダーを含むプロジェクト内のほぼすべてのcppファイルについて、このセットが表示されます。構造体と構造体変数の定義をcppファイルに移動しようとしましたが、他のcppファイルはそれらを表示または操作できません。これは、プロジェクト内のすべてのファイルが機能する必要があるため、非常に重要です。これらの構造体で。

しかし、この問題について最も紛らわしい部分は、もう少し説明が必要です。

このプロジェクトでこれらの複数のファイルを設定する方法は、私が扱っている本、John S.HarbourによるAllIn One GameProgrammingと同じです。同じプロジェクトの複数のcppsに含まれる1つのヘッダーを必要とする本のプロジェクトなどのファイルを作成したときに、まったく同じ問題が発生しました。

私はそれらをタイプすることができました、本からの単語のための単語、そして私は単語のための単語を意味します...
そして、プロジェクト内のすべてのcppに対して一連のMDエラーが発生します。

本に含まれているCDからサンプルプロジェクトをロードした場合、ファイル自体とプロジェクトオプションはすべて私が作成したものと同じ外観でしたが、問題なくコンパイルおよび実行されました。

独自のプロジェクトファイルを作成し、サンプルプロジェクトのソースファイルとヘッダーファイルをCDから追加しただけでは、これもコンパイルして実行されますが、これらと私の違いはわかりません。

そこで、自分でプロジェクトファイルを作成し、空のソースファイルとヘッダーファイルを作成して追加し、対応する予定のCDのファイルから内容をコピーして貼り付けて入力しました(同じ働いていたもの)。そして確かに、私は同じことを得るでしょう... MDエラーメッセージの行と行。

私は絶対に困惑しています。私はこれらすべての方法を複数回繰り返しましたが、コードの入力ミスやコピーミスはないと確信しています。事前に作成されたファイル自体に何かがあるようです。いくつかの構成設定または私が完全に欠落している他の何か...それは私が自分で作成したファイルがコンパイルしない間、それらを正しくコンパイルする原因になります。

15
Wolerywol

これらの変数をヘッダーファイルで宣言し、ヘッダーファイルを各C++ファイルにインクルードしているため、各C++ファイルにはそれらの独自のコピーがあります。

これを回避する通常の方法は、ヘッダーファイル内の変数をnot宣言することです。代わりに、それらを単一のC++ファイルで宣言し、それらが必要になる可能性のある他のすべてのファイルでexternとして宣言します。

私が以前にこれを処理した別の方法、一部の人々は不快だと思うかもしれません...次のようにヘッダーファイルでそれらを宣言します:

#ifdef MAINFILE
    #define EXTERN
#else
    #define EXTERN extern
#endif

EXTERN MYSTRUCT Job_Grunt;
EXTERN MYSTRUCT *Grunt = &Job_Grunt;
EXTERN MYSTRUCT Job_Uruk;
EXTERN MYSTRUCT *Uruk = &Job_Uruk;

次に、C++ファイルのoneに、...を追加します。

#define MAINFILE

... #include行の前。これですべてが処理され、(私の個人的な意見では)すべてのファイルのすべての変数を再宣言するよりもはるかに優れています。

もちろん、realの解決策は、グローバル変数をまったく使用しないことですが、始めたばかりの場合、それを実現するのは困難です。

23
Head Geek

変数を定義すると、コンパイラはその変数用のメモリを確保します。ヘッダーファイルで変数を定義し、そのファイルをすべてのソースファイルに含めることで、複数のファイルで同じ変数を定義することになります。

変数定義の前にキーワードexternを置くと、この変数はすでにどこかで定義されており、宣言しているだけであることをコンパイラーに通知します。他のファイルが使用できるように変数に名前を付ける(つまり名前を付ける)。

したがって、ヘッダーファイルで、externキーワードを追加して、すべての定義前方宣言を作成する必要があります。

extern MYSTRUCT Job_Grunt;
extern MYSTRUCT *Grunt;
extern MYSTRUCT Job_Uruk;
extern MYSTRUCT *Uruk;

extern int Other_data[100];

そして、ソースファイルのone(そして1つだけ)で、変数を通常どおりに定義します。

MYSTRUCT Job_Grunt;
MYSTRUCT *Grunt = &Job_Grunt;
MYSTRUCT Job_Uruk;
MYSTRUCT *Uruk = &Job_Grunt;

int Other_data[100];
12
Jeremy Ruten

他のほとんどの回答は、複数の定義が表示される理由については正しいものですが、用語は不正確です。宣言と定義を理解することが問題の鍵です。

宣言はアイテムの存在をアナウンスしますが、インスタンス化は引き起こしません。したがって、externステートメントは宣言であり、定義ではありません。

定義は、定義されたアイテムのインスタンスを作成します。したがって、ヘッダーに定義がある場合、それは各.cppファイルでインスタンス化され、複数の定義になります。定義も宣言です。つまり、たとえばアイテムのスコープが1つの.cppファイルに制限されている場合、個別の宣言は必要ありません。

注:ここでのWordのインスタンス化の使用は、実際にはデータ項目にのみ適用されます。

6
Steve Fallows

ヘッダーファイルで変数をexternとして定義してから、cppファイルでも定義する必要があります。つまり:

extern MYSTRUCT Job_Grunt;

ヘッダーファイルで、次にプロジェクトのcppファイルで通常どおり宣言します。

ヘッダーファイルは定義専用です。ヘッダーファイルで変数をインスタンス化すると、ヘッダーがプロジェクトに含まれるたびにインスタンス化が試みられます。 externディレクティブを使用すると、それは単なる定義であり、インスタンス化は別の場所で行われることがコンパイラに通知されます。

5
Gerald

.hファイルで定義された関数でもこのエラーが発生しました。ヘッダーファイルは、クラスの宣言ではなく、プロジェクトのさまざまな場所で必要となるいくつかの関数の定義を行うことを目的としていました。 (「定義」と「宣言」の使用法を混同するかもしれませんが、主なアイデアを与えることができればと思います。)「複数の定義」エラーを与える関数の定義の直前に「インライン」キーワードを置くと、エラーが回避されます。

2
bpamuk

ジェラルドが言ったことを拡張するために、ヘッダーは構造体のインスタンスを定義しています(これはあなたが望むものではありません)。これにより、ヘッダーを含む各コンパイルユニット(cppファイル)が独自のバージョンの構造体インスタンスを取得し、リンク時に問題が発生します。

Geraldが言ったように、ヘッダーで構造体への参照を定義し( 'extern'を使用)、インスタンスをインスタンス化する1つのcppファイルをプロジェクトに含める必要があります。

0
Nick

私もしばらく前にこの問題を抱えていました。何がそれを解決したのか説明しようと思います。すべての宣言があり、すべてのcppファイルに含める必要があるglobal.hファイルがありました。すべての.cppに含める代わりに、.hに含めました。すべての「.h」ファイルに#ifndefと#defineの行を追加し、#endifで終了しました。これでMDの問題が解決しました。これがあなたにもうまくいくことを願っています。

0
Anjum

これが私にとってうまくいったことです。ソースを別々のライブラリにリンクすることです。 (私の問題は、プログラムの作成ではなく、1つまたは複数のライブラリでした。)次に、1つのプログラムを作成したtwoライブラリに(正常に)リンクしました。

同じソースファイルに2セットの関数(一方が他方に依存)があり、同じ単一のヘッダーファイルで宣言されました。次に、2つの関数セットを2つのヘッダー+ソースファイルに分けようとしました。

私は両方の#pragmaを一度試し、#ifndef ... #define ...#endifでガードを含めました。また、ヘッダーファイルで変数と関数をexternとして定義しました。

Steve Fallowsが指摘したように、問題はコンパイルではなく、リンケージにあります。私の特定の問題では、それぞれが独自のソースファイルにある2セットの関数をコンパイルしてから、2つの別々のライブラリにリンクすることで回避できました。

g++ -o grandfather.o -c grandfather.cpp
g++ -o father.o -c father.cpp
g++ -fPIC -shared -o libgf.so grandfather.o
g++ -fPIC -shared -o libfather.so father.o

これにより、プログラムをlibgf.soとlibfather.soの両方にリンクする必要があります。私の特定のケースでは、違いはありません。しかし、そうでなければ私は彼らを一緒に働かせることができませんでした。

0
jbatista