C++ #include-Wächter

Lesezeit: 10 Minuten

C include Wachter
Orujimaru

GELÖST

Was mir wirklich geholfen hat, war, dass ich Header in die .cpp-Datei einschließen konnte, ohne den neu definierten Fehler zu verursachen.


Ich bin neu in C++, aber ich habe einige Programmiererfahrung in C# und Java, daher könnte mir etwas Grundlegendes fehlen, das einzigartig in C++ ist.

Das Problem ist, dass ich nicht wirklich weiß, was falsch ist, ich werde etwas Code einfügen, um zu versuchen, das Problem zu erklären.

Ich habe drei Klassen, GameEvents, Physics und GameObject. Ich habe Header für jeden von ihnen. GameEvents hat eine Physik und eine Liste von GameObjects. Physik hat eine Liste von GameObjects.

Was ich erreichen möchte, ist, dass GameObject in der Lage sein soll, auf ein Physikobjekt zuzugreifen oder es zu besitzen.

Wenn ich einfach “Physics.h” in GameObject #einschließe, erhalte ich den “Fehler C2111: ‘ClassXXX’ : ‘class’ type reddefinition”, den ich verstehe. Und hier dachte ich, dass #include-guards helfen würden, also fügte ich meiner Physics.h einen include guard hinzu, da dies der Header ist, den ich zweimal einfügen möchte.

So sieht es aus

#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

Aber wenn ich “Physics.h” jetzt so in meine GameObject.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();
};

Ich erhalte mehrere Probleme, die nicht verstehen, warum sie angezeigt werden. Wenn ich “Physics.h” nicht #einschließe, läuft mein Code einwandfrei.

Ich bin für jede Hilfe sehr dankbar.

  • Duplikat von stackoverflow.com/questions/8010601/… und vielen, vielen anderen.

    – IronMensan

    5. November 2011 um 14:24 Uhr

1646997611 693 C include Wachter
Shahbaz

Der Präprozessor ist ein Programm, das Ihr Programm nimmt, einige Änderungen vornimmt (z. B. Dateien einschließen (#include), Makroerweiterung (#define) und im Grunde alles, was mit beginnt #) und gibt das “saubere” Ergebnis an den Compiler.

Der Präprozessor arbeitet so, wenn er sieht #include:

Wenn du schreibst:

#include "some_file"

Die Inhalte von some_file erhalten Sie fast buchstäblich eine Kopie, die in die Datei eingefügt wird, die sie enthält. Nun, wenn Sie haben:

a.h:
class A { int a; };

Und:

b.h:
#include "a.h"
class B { int b; };

Und:

main.cpp:
#include "a.h"
#include "b.h"

Du erhältst:

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"

Jetzt können Sie sehen, wie A wird neu definiert.

Wenn Sie Wachen schreiben, werden sie wie folgt:

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

Schauen wir uns nun an, wie #includes in main würde erweitert werden (dies ist genau wie im vorherigen Fall: Kopieren und Einfügen)

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

Folgen wir nun dem Präprozessor und sehen, welcher “echte” Code dabei herauskommt. Ich gehe Zeile für Zeile vor:

// From #include "a.h"

Kommentar. Ignorieren! Fortsetzen:

#ifndef A_H

Ist A_H definiert? Nein! Dann fahre fort:

#define A_H

OK jetzt A_H ist definiert. Fortsetzen:

class A { int a; };

Das ist nichts für Präprozessoren, also lass es einfach sein. Fortsetzen:

#endif

Der Vorherige if fertig hier. Fortsetzen:

// From #include "b.h"

Kommentar. Ignorieren! Fortsetzen:

#ifndef B_H

Ist B_H definiert? Nein! Dann fahre fort:

#define B_H

OK jetzt B_H ist definiert. Fortsetzen:

#ifndef A_H          // From

Ist A_H definiert? JAWOHL! Dann ignorieren, bis entsprechend #endif:

#define A_H          // #include "a.h"

Ignorieren

class A { int a; };  // inside

Ignorieren

#endif               // "b.h"

Der Vorherige if fertig hier. Fortsetzen:

class B { int b; };

Das ist nichts für Präprozessoren, also lass es einfach sein. Fortsetzen:

#endif

Der Vorherige if fertig hier.

Das heißt, nachdem der Präprozessor mit der Datei fertig ist, sieht der Compiler Folgendes:

main.cpp
class A { int a; };
class B { int b; };

Also, wie Sie sehen können, alles, was Sie bekommen können #included in der gleichen Datei zweimal, ob direkt oder indirekt geschützt werden muss. Seit .h Da es sehr wahrscheinlich ist, dass Dateien zweimal enthalten sind, ist es gut, wenn Sie ALLE Ihre .h-Dateien schützen.

PS Beachten Sie, dass Sie auch Rundschreiben haben #includeS. Stellen Sie sich vor, der Präprozessor kopiert den Code von Physics.h in GameObject.h, was sieht, dass es eine gibt #include "GameObject.h" was bedeutet kopieren GameObject.h in sich. Wenn Sie kopieren, erhalten Sie erneut #include "Pysics.h" und du steckst für immer in einer Schleife fest. Compiler verhindern das, aber das bedeutet Ihre #includes sind halb fertig.

Bevor Sie sagen, wie Sie dies beheben können, sollten Sie etwas anderes wissen.

Wenn Sie haben:

#include "b.h"

class A
{
    B b;
};

Dann muss der Compiler alles darüber wissen bam wichtigsten, welche Variablen es hat usw., damit es weiß, wie viele Bytes es ersetzen sollte b in A.

Wenn Sie jedoch Folgendes haben:

class A
{
    B *b;
};

Dann muss der Compiler nicht wirklich etwas darüber wissen B (da Zeiger unabhängig vom Typ dieselbe Größe haben). Das einzige, was es wissen muss B ist, dass es existiert!

Sie tun also etwas, das als “Forward-Deklaration” bezeichnet wird:

class B;  // This line just says B exists

class A
{
    B *b;
};

Dies ist vielen anderen Dingen sehr ähnlich, die Sie in Header-Dateien tun, wie zum Beispiel:

int function(int x);  // This is forward declaration

class A
{
public:
    void do_something(); // This is forward declaration
}

  • Danke, das hat mir sehr geholfen und ich verstehe besser, was passiert. Ich habe erfolgreich eine Physik *physics in meiner GameObject.h weiterleiten deklariert. aber wenn ich es richtig verstehe, kann ich keine Physik in meinem GameObject erstellen, ich muss einen vorhandenen Zeiger auf mein GameObject.cpp übergeben?

    – Orujimaru

    5. November 2011 um 14:54 Uhr

  • Ich kann anscheinend nicht verstehen, wie ich auf den Physik-Zeiger in meiner GameObject.cpp zugreife. Wenn ich es mit Physics->CheckCollisions() versuche, erhalte ich “Zeiger auf unvollständigen Klassentyp ist nicht zulässig” und es wurde kein Mitglied gefunden.

    – Orujimaru

    5. November 2011 um 14:58 Uhr

  • @Orujimaru Ich sehe keine Physics Objekt oder Zeiger in Ihrem GameObject Klasse, aber ich sehe eine Menge GameObject *ist in deinem Physics Klasse. Dies deutet auf eine Vorwärtsdeklaration hin GameObject in Physics.h eher als umgekehrt. So oder so, wenn Sie die Klasse verwenden möchten (d. h. in der .cpp Dateien), müssen Sie jeden Header selbst einschließen. Da Sie nicht enthalten .cpp Dateien besteht keine Gefahr von zirkulären Includes!

    – Shahbaz

    5. November 2011 um 17:25 Uhr

  • @hans, nein nein, die Definition eines Symbols durch #define ist in derselben Zeile enthalten. Zum Beispiel #define SOME_SYMBOL (some_expression) bedeutet, dass der Präprozessor ersetzen würde SOME_SYMBOL wo es von diesem Punkt an mit sieht (some_expression). Wenn Sie haben #define SOME_SYMBOL mit nichts davor würde es jedes Mal, wenn es es sieht, durch nichts ersetzen. Damit #define A_H bedeutet “definiere ein Symbol namens A_H was durch nichts ersetzt”. Der für uns wichtige Teil ist das “Definiere ein Symbol namens A_H“, weil wir später ein if haben wollen, ob das Symbol definiert ist (#ifndef).

    – Shahbaz

    17. März 2014 um 13:02 Uhr

  • Damit, class A {int a} hat nichts zu tun mit #define A_H und gehört wirklich dazu #ifndef...#endif' right? Also #define A_H` mit A_H Nichts zugewiesen zu bekommen, ist nur dazu da, formal etwas zu machen A_H definiert, oder?

    – Hans

    17. März 2014 um 13:20 Uhr

Sie haben hier Zirkelbezüge: Physics.h beinhaltet GameObject.h welches beinhaltet Physics.h. Deine Klasse Physics Verwendet GameObject* (Zeiger) Typ, so dass Sie es nicht einschließen müssen GameObject.h in Physics.h aber verwenden Sie einfach die Vorwärtsdeklaration – anstelle von

#include "GameObject.h" 

setzen

class GameObject;   

Fügen Sie außerdem Guards in jede Header-Datei ein.

Das Problem ist, dass Sie GameObject.h hat keine Wachen, also wenn Sie #include "GameObject.h" in Physics.h es wird eingeschlossen, wenn GameObject.h beinhaltet Physics.h.

Fügen Sie Wachen in alle Ihre hinzu *.h oder *.hh Header-Dateien (es sei denn, Sie haben spezielle Gründe, dies nicht zu tun).

Um zu verstehen, was passiert, versuchen Sie, die vorverarbeitete Form Ihres Quellcodes zu erhalten. Mit GCC ist es so etwas wie g++ -Wall -C -E yourcode.cc > yourcode.i (Ich habe keine Ahnung, wie Microsoft-Compiler das machen). Sie können auch fragen, welche Dateien enthalten sind, mit GCC als g++ -Wall -H -c yourcode.cc

Zuerst müssen Sie auch Guards auf GameObject einbinden, aber das ist hier nicht das eigentliche Problem

Wenn etwas anderes zuerst „physics.h“ enthält, „physics.h“ enthält „gameobject.h“, erhalten Sie etwa Folgendes:

class GameObject {
...
};

#include physics.h

class Physics {
...
};

und die #include-physics.h wird wegen der include-Wächter verworfen, und Sie erhalten eine Deklaration von GameObject vor der Deklaration von Physics.

Aber das ist ein Problem, wenn Sie möchten, dass GameObject einen Zeiger auf eine Physik hat, denn dafür müsste die Physik zuerst deklariert werden.

Um den Zyklus aufzulösen, können Sie stattdessen eine Klasse vorwärts deklarieren, aber nur, wenn Sie sie nur als Zeiger oder Referenz in der folgenden Deklaration verwenden, dh:

#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

  • Ich habe Schutzvorrichtungen für alle Header hinzugefügt, aber wenn ich #include “GameObject.h” durch Class GameObject ersetze; Visual Studio gibt Fehler für jede einzelne Codezeile in Physics.cpp und Physics.h zurück. Dasselbe passiert, wenn ich versuche, eine andere Klasse in einem beliebigen Header weiterzuleiten.

    – Orujimaru

    5. November 2011 um 14:42 Uhr

Verwenden Sie Guards in ALLE Ihre Header-Dateien. Da Sie Visual Studio verwenden, können Sie die #pragma once als erste Präprozessordefinition in allen Ihren Headern.

Ich schlage jedoch vor, den klassischen Ansatz zu verwenden:

#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_

// Header code here

#endif //CLASS_NAME_H_

Lesen Sie zum zweiten Mal darüber Vorwärtserklärung und wende es an.

  • Ich habe Schutzvorrichtungen für alle Header hinzugefügt, aber wenn ich #include “GameObject.h” durch Class GameObject ersetze; Visual Studio gibt Fehler für jede einzelne Codezeile in Physics.cpp und Physics.h zurück. Dasselbe passiert, wenn ich versuche, eine andere Klasse in einem beliebigen Header weiterzuleiten.

    – Orujimaru

    5. November 2011 um 14:42 Uhr

C include Wachter
angarciaba

Das Ziel eines Header Guard ist es, zu vermeiden, dass dieselbe Datei mehrmals eingefügt wird. Aber der Header-Guard, der derzeit in C++ verwendet wird, kann verbessert werden. Die aktuelle Wache ist:

#ifndef AAA_H
#define AAA_H

class AAA
{ /* ... */ };

#endif

Mein neuer Schutzvorschlag lautet:

#ifndef AAA_H
#define AAA_H

class AAA
{ /* ... */ };

#else
class AAA;  // Forward declaration
#endif

Dies löst das lästige Problem, das auftritt, wenn die AAA-Klasse die BBB-Klassendeklaration benötigt, während die BBB-Klasse die AAA-Klassendeklaration benötigt, typischerweise weil es gekreuzte Zeiger von einer Klasse zur anderen gibt:

// 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

Ich würde es lieben, wenn dies in die IDEs aufgenommen würde, die automatisch Code aus Vorlagen generieren.

990660cookie-checkC++ #include-Wächter

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy