web-dev-qa-db-ja.com

C ++ #includeガード

解決済み

本当に助けになったのは、再定義されたエラーを引き起こさずに.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しない場合、コードは正常に実行されます。

私はどんな助けにも感謝しています。

39
Orujimaru

プリプロセッサは、プログラムを受け取り、いくつかの変更(たとえば、インクルードファイル(#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の#includesがどのように展開されるかを見てみましょう(これは、前の場合とまったく同じです:コピーと貼り付け)

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; };

ご覧のとおり、直接または間接的に保護する必要があるかどうかにかかわらず、同じファイルで#includedを2回取得できるものはすべてです。 .hファイルは常に2回インクルードされる可能性が非常に高いため、すべての.hファイルを保護することをお勧めします。

追伸循環#includesもあることに注意してください。プリプロセッサがPhysics.hのコードをコピーしてGameObject.hにコピーし、#include "GameObject.h"があることを想像してください。これはGameObject.hを自分自身にコピーすることを意味します。コピーすると、再び#include "Pysics.h"を取得し、永久にループに陥ります。コンパイラはそれを防ぎますが、それはあなたの#includesが半分行われたことを意味します。

これを修正する方法を言う前に、別のことを知っておく必要があります。

あなたが持っている場合:

#include "b.h"

class A
{
    B b;
};

次に、コンパイラーは、bのすべての情報、最も重要なこととして、どの変数があるかなどを知って、bAの代わりに何バイト置くべきかを知る必要があります。

ただし、次の場合:

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
}
118
Shahbaz

ここに循環参照があります:Physics.hGameObject.hを含むPhysics.hを含みます。クラスPhysicsGameObject*(ポインター)型を使用するため、GameObject.hPhysics.hに含める必要はなく、代わりに前方宣言を使用するだけです。

#include "GameObject.h" 

プット

class GameObject;   

さらに、各ヘッダーファイルにガードを配置します。

5
Bojan Komazec

問題は、GameObject.hにガードがないため、#include "GameObject.h"Physics.hが含まれる場合、GameObject.hPhysics.hが含まれる場合に含まれます。

4
bitmask

すべての*.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
4
je4d

ヘッダーファイルで[〜#〜] all [〜#〜]にインクルードガードを使用します。 Visual Studioを使用しているため、#pragma onceすべてのヘッダーの最初のプリプロセッサ定義として。

ただし、古典的なアプローチを使用することをお勧めします。

#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_

// Header code here

#endif //CLASS_NAME_H_

2番目の 前方宣言 を読んで適用します。

3
pnezis

ヘッダーガードの目的は、同じファイルを何度も含めることを避けることです。ただし、現在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にこれが含まれることを楽しみにしています。

0
angarciaba