解決済み
本当に助けになったのは、再定義されたエラーを引き起こさずに.cppファイルにヘッダーを#includeできることでした。
私はC++を初めて使用しますが、C#およびJavaでプログラミングの経験があります。そのため、C++に固有の基本的なものが欠けている可能性があります。
問題は、何が悪いのか本当にわからないということです。問題を説明するためにいくつかのコードを貼り付けます。
GameEvents、Physics、GameObjectの3つのクラスがあります。それぞれにヘッダーがあります。 GameEventsには、1つの物理学とGameObjectsのリストがあります。物理学にはGameObjectsのリストがあります。
私が達成しようとしているのは、GameObjectがPhysicsオブジェクトにアクセスまたは所有できるようにすることです。
GameObjectに「Physics.h」を#includeするだけの場合、「エラーC2111: 'ClassXXX': 'class' type redifinition」が表示されます。これが#include-guardsが役立つと思った場所です。したがって、Physics.hにインクルードガードを追加しました。
これは見た目です
#ifndef PHYSICS_H
#define PHYSICS_H
#include "GameObject.h"
#include <list>
class Physics
{
private:
double gravity;
list<GameObject*> objects;
list<GameObject*>::iterator i;
public:
Physics(void);
void ApplyPhysics(GameObject*);
void UpdatePhysics(int);
bool RectangleIntersect(SDL_Rect, SDL_Rect);
Vector2X CheckCollisions(Vector2X, GameObject*);
};
#endif // PHYSICS_H
ただし、GameObject.hに「Physics.h」を#includeすると、次のようになります。
#include "Texture2D.h"
#include "Vector2X.h"
#include <SDL.h>
#include "Physics.h"
class GameObject
{
private:
SDL_Rect collisionBox;
public:
Texture2D texture;
Vector2X position;
double gravityForce;
int weight;
bool isOnGround;
GameObject(void);
GameObject(Texture2D, Vector2X, int);
void UpdateObject(int);
void Draw(SDL_Surface*);
void SetPosition(Vector2X);
SDL_Rect GetCollisionBox();
};
なぜそれらが現れているのか理解できない複数の問題があります。 「Physics.h」を#includeしない場合、コードは正常に実行されます。
私はどんな助けにも感謝しています。
プリプロセッサは、プログラムを受け取り、いくつかの変更(たとえば、インクルードファイル(#include)、マクロ展開(#define)、および基本的に#
で始まるすべて)を行い、「クリーン」な結果を与えるプログラムです。コンパイラ。
プリプロセッサは、#include
を検出すると次のように機能します。
あなたが書くとき:
#include "some_file"
some_file
の内容は、ほぼ文字通り、コピーをそれを含むファイルに貼り付けます。あなたが持っている場合:
a.h:
class A { int a; };
そして:
b.h:
#include "a.h"
class B { int b; };
そして:
main.cpp:
#include "a.h"
#include "b.h"
あなたが得る:
main.cpp:
class A { int a; }; // From #include "a.h"
class A { int a; }; // From #include "b.h"
class B { int b; }; // From #include "b.h"
これで、A
の再定義方法を確認できます。
ガードを作成すると、次のようになります。
a.h:
#ifndef A_H
#define A_H
class A { int a; };
#endif
b.h:
#ifndef B_H
#define B_H
#include "a.h"
class B { int b; };
#endif
それでは、mainの#include
sがどのように展開されるかを見てみましょう(これは、前の場合とまったく同じです:コピーと貼り付け)
main.cpp:
// From #include "a.h"
#ifndef A_H
#define A_H
class A { int a; };
#endif
// From #include "b.h"
#ifndef B_H
#define B_H
#ifndef A_H // From
#define A_H // #include "a.h"
class A { int a; }; // inside
#endif // "b.h"
class B { int b; };
#endif
それでは、プリプロセッサをフォローして、これからどのような「実際の」コードが出てくるかを見てみましょう。行ごとに行きます:
// From #include "a.h"
コメント。無視!持続する:
#ifndef A_H
A_H
は定義されていますか?いや!次に続行します。
#define A_H
OK A_H
が定義されました。持続する:
class A { int a; };
これはプリプロセッサ向けではないため、そのままにしておきます。持続する:
#endif
前のif
はここで終了しました。持続する:
// From #include "b.h"
コメント。無視!持続する:
#ifndef B_H
B_H
は定義されていますか?いや!次に続行します。
#define B_H
OK B_H
が定義されました。持続する:
#ifndef A_H // From
A_H
は定義されていますか?はい!次に、対応する#endif
まで無視します:
#define A_H // #include "a.h"
無視する
class A { int a; }; // inside
無視する
#endif // "b.h"
前のif
はここで終了しました。持続する:
class B { int b; };
これはプリプロセッサ向けではないため、そのままにしておきます。持続する:
#endif
前のif
はここで終了しました。
つまり、プリプロセッサがファイルを処理した後、これはコンパイラが見るものです。
main.cpp
class A { int a; };
class B { int b; };
ご覧のとおり、直接または間接的に保護する必要があるかどうかにかかわらず、同じファイルで#include
dを2回取得できるものはすべてです。 .h
ファイルは常に2回インクルードされる可能性が非常に高いため、すべての.hファイルを保護することをお勧めします。
追伸循環#include
sもあることに注意してください。プリプロセッサがPhysics.hのコードをコピーしてGameObject.hにコピーし、#include "GameObject.h"
があることを想像してください。これはGameObject.h
を自分自身にコピーすることを意味します。コピーすると、再び#include "Pysics.h"
を取得し、永久にループに陥ります。コンパイラはそれを防ぎますが、それはあなたの#include
sが半分行われたことを意味します。
これを修正する方法を言う前に、別のことを知っておく必要があります。
あなたが持っている場合:
#include "b.h"
class A
{
B b;
};
次に、コンパイラーは、b
のすべての情報、最も重要なこととして、どの変数があるかなどを知って、b
のA
の代わりに何バイト置くべきかを知る必要があります。
ただし、次の場合:
class A
{
B *b;
};
そうすれば、コンパイラはB
について何も知る必要がありません(型に関係なくポインタは同じサイズなので)。 B
について知る必要があるのは、それが存在することだけです!
したがって、「前方宣言」と呼ばれることを行います。
class B; // This line just says B exists
class A
{
B *b;
};
これは、次のようなヘッダーファイルで行う他の多くのことと非常に似ています。
int function(int x); // This is forward declaration
class A
{
public:
void do_something(); // This is forward declaration
}
ここに循環参照があります:Physics.h
はGameObject.h
を含むPhysics.h
を含みます。クラスPhysics
はGameObject*
(ポインター)型を使用するため、GameObject.h
をPhysics.h
に含める必要はなく、代わりに前方宣言を使用するだけです。
#include "GameObject.h"
プット
class GameObject;
さらに、各ヘッダーファイルにガードを配置します。
問題は、GameObject.h
にガードがないため、#include "GameObject.h"
にPhysics.h
が含まれる場合、GameObject.h
にPhysics.h
が含まれる場合に含まれます。
すべての*.h
または*.hh
ヘッダーファイルにインクルードガードを追加します(そうしない特別な理由がない限り)。
何が起こっているのかを理解するために、ソースコードの前処理された形式を取得してください。 GCCの場合、それはg++ -Wall -C -E yourcode.cc > yourcode.i
のようなものです(Microsoftコンパイラーがそれをどのように行うかについてはわかりません)。 GCCをg++ -Wall -H -c yourcode.cc
として、どのファイルが含まれているかを尋ねることもできます
まず、ゲームオブジェクトにもガードを含める必要がありますが、それはここでの本当の問題ではありません
他に何かがphysics.hを含む場合、physics.hにはgameobject.hが含まれる場合、次のようになります。
class GameObject {
...
};
#include physics.h
class Physics {
...
};
#include physics.hはインクルードガードのために破棄され、Physicsの宣言の前にGameObjectの宣言になります。
しかし、htatでは物理を最初に宣言する必要があるため、GameObjectに物理へのポインタを持たせたい場合、これは問題です。
サイクルを解決するには、代わりにクラスを前方宣言することができますが、次の宣言でポインタまたは参照としてそれを使用している場合のみです:
#ifndef PHYSICS_H
#define PHYSICS_H
// no need for this now #include "GameObject.h"
#include <list>
class GameObject;
class Physics
{
private:
list<GameObject*> objects;
list<GameObject*>::iterator i;
public:
void ApplyPhysics(GameObject*);
Vector2X CheckCollisions(Vector2X, GameObject*);
};
#endif // PHYSICS_H
ヘッダーファイルで[〜#〜] all [〜#〜]にインクルードガードを使用します。 Visual Studioを使用しているため、#pragma once
すべてのヘッダーの最初のプリプロセッサ定義として。
ただし、古典的なアプローチを使用することをお勧めします。
#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_
// Header code here
#endif //CLASS_NAME_H_
2番目の 前方宣言 を読んで適用します。
ヘッダーガードの目的は、同じファイルを何度も含めることを避けることです。ただし、現在C++で使用されているヘッダーガードは改善できます。現在のガードは次のとおりです。
#ifndef AAA_H
#define AAA_H
class AAA
{ /* ... */ };
#endif
私の新しいガードの提案は次のとおりです。
#ifndef AAA_H
#define AAA_H
class AAA
{ /* ... */ };
#else
class AAA; // Forward declaration
#endif
これは、AAAクラスがBBBクラス宣言を必要とする一方で、BBBクラスがAAAクラス宣言を必要とするときに発生する厄介な問題を解決します。これは、通常、あるクラスから別のクラスへのクロスポインターがあるためです:
// File AAA.h
#ifndef AAA_H
#define AAA_H
#include "BBB.h"
class AAA
{
BBB *bbb;
/* ... */
};
#else
class AAA; // Forward declaration
#endif
//+++++++++++++++++++++++++++++++++++++++
// File BBB.h
#ifndef BBB_H
#define BBB_H
#include "AAA.h"
class BBB
{
AAA *aaa;
/* ... */
};
#else
class BBB; // Forward declaration
#endif
テンプレートからコードを自動的に生成するIDEにこれが含まれることを楽しみにしています。