C ++ # include protezioni

RISOLTO

Ciò che mi ha veramente aiutato è stato che potevo #includere le intestazioni nel file .cpp senza causare l’errore ridefinito.


Sono nuovo di C ++, ma ho una certa esperienza di programmazione in C # e Java, quindi potrei mancare qualcosa di base che è unico per C ++.

Il problema è che non so davvero cosa c’è che non va, quindi incollerò del codice per cercare di spiegare il problema.

Ho tre Classes, GameEvents, Physics e GameObject. Ho intestazioni per ciascuno di essi. GameEvents ha una fisica e un elenco di oggetti GameObjects. La fisica ha un elenco di oggetti GameObjects.

Quello che sto cercando di ottenere è che voglio che GameObject sia in grado di accedere o possedere un object di fisica.

Se ho semplicemente #include “Physics.h” in GameObject ottengo l’errore “C2111: ‘ClassXXX’: ‘class’ type redifinition” che capisco. Ed è qui che pensavo che le # include-guards mi avrebbero aiutato così ho aggiunto una guardia di inclusione al mio Physics.h dal momento che questa è l’intestazione che voglio includere due volte.

Ecco come appare

#ifndef PHYSICS_H #define PHYSICS_H #include "GameObject.h" #include  class Physics { private: double gravity; list objects; list::iterator i; public: Physics(void); void ApplyPhysics(GameObject*); void UpdatePhysics(int); bool RectangleIntersect(SDL_Rect, SDL_Rect); Vector2X CheckCollisions(Vector2X, GameObject*); }; #endif // PHYSICS_H 

Ma se ho #include “Physics.h” nel mio GameObject.h ora mi piace questo:

 #include "Texture2D.h" #include "Vector2X.h" #include  #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(); }; 

Ottengo più problemi che non capisco perché vengano visualizzati. Se non lo faccio #include “Physics.h” il mio codice funziona perfettamente.

Sono molto grato per qualsiasi aiuto.

Il preprocessore è un programma che prende il tuo programma, apporta alcune modifiche (ad esempio include i file (#include), l’espansione delle macro (#define) e in pratica tutto ciò che inizia con # ) e restituisce il risultato “pulito” al compilatore.

Il preprocessore funziona come questo quando vede #include :

Quando scrivi:

 #include "some_file" 

I contenuti di some_file letteralmente copiati nel file incluso. Ora se hai:

 ah: class A { int a; }; 

E:

 bh: #include "ah" class B { int b; }; 

E:

 main.cpp: #include "ah" #include "bh" 

Ottieni:

 main.cpp: class A { int a; }; // From #include "ah" class A { int a; }; // From #include "bh" class B { int b; }; // From #include "bh" 

Ora puoi vedere come A viene ridefinito.

Quando scrivi guardie, diventano così:

 ah: #ifndef A_H #define A_H class A { int a; }; #endif bh: #ifndef B_H #define B_H #include "ah" class B { int b; }; #endif 

Quindi ora vediamo come #include s in main sarebbe espanso (questo è esattamente, come nel caso precedente: copia-incolla)

 main.cpp: // From #include "ah" #ifndef A_H #define A_H class A { int a; }; #endif // From #include "bh" #ifndef B_H #define B_H #ifndef A_H // From #define A_H // #include "ah" class A { int a; }; // inside #endif // "bh" class B { int b; }; #endif 

Ora seguiamo il preprocessore e vediamo che cosa viene fuori dal codice “reale”. Andrò linea per linea:

 // From #include "ah" 

Commento. Ignorare! Continua:

 #ifndef A_H 

A_H è definito? No! Quindi continua:

 #define A_H 

Ok ora A_H è definito. Continua:

 class A { int a; }; 

Questo non è qualcosa per il preprocessore, quindi lascia perdere. Continua:

 #endif 

Il precedente if finito qui. Continua:

 // From #include "bh" 

Commento. Ignorare! Continua:

 #ifndef B_H 

B_H è definito? No! Quindi continua:

 #define B_H 

Ok ora B_H è definito. Continua:

 #ifndef A_H // From 

A_H è definito? SÌ! Quindi ignora fino al corrispondente #endif :

 #define A_H // #include "ah" 

Ignorare

 class A { int a; }; // inside 

Ignorare

 #endif // "bh" 

Il precedente if finito qui. Continua:

 class B { int b; }; 

Questo non è qualcosa per il preprocessore, quindi lascia perdere. Continua:

 #endif 

Il precedente if finito qui.

Cioè, dopo che il preprocessore ha finito con il file, questo è ciò che il compilatore vede:

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

Quindi, come puoi vedere, tutto ciò che può ottenere #include d nello stesso file due volte, se direttamente o indirettamente deve essere sorvegliato. Dal momento che i file .h sono sempre molto probabilmente inclusi due volte, è un bene se si custodiscono TUTTI i file .h.

PS Nota che hai anche #include s circolare. Immagina il preprocessore copiando il codice di Physics.h in GameObject.h che vede che c’è un #include "GameObject.h" che significa copiare GameObject.h in se stesso. Quando copi, ottieni di nuovo #include "Pysics.h" e sei bloccato in un ciclo per sempre. I compilatori lo impediscono, ma ciò significa che i tuoi #include sono a metà.

Prima di dire come risolvere il problema, dovresti sapere un’altra cosa.

Se hai:

 #include "bh" class A { B b; }; 

Quindi il compilatore ha bisogno di sapere tutto su b , soprattutto, quali variabili ha ecc. In modo che sappia quanti byte dovrebbe mettere al posto di b in A

Tuttavia, se hai:

 class A { B *b; }; 

Quindi il compilatore non ha davvero bisogno di sapere nulla di B (poiché i puntatori, indipendentemente dal tipo hanno le stesse dimensioni). L’unica cosa che deve sapere su B è che esiste!

Quindi fai qualcosa chiamato “forward statement”:

 class B; // This line just says B exists class A { B *b; }; 

Questo è molto simile a molte altre cose che fai nei file header come:

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

Hai riferimenti circolari qui: Physics.h include GameObject.h che include Physics.h . La tua class Physics usa il tipo GameObject* (puntatore) in modo da non dover includere GameObject.h in Physics.h ma usa solo la dichiarazione forward – invece di

 #include "GameObject.h" 

mettere

 class GameObject; 

Inoltre, metti guardie in ogni file di intestazione.

Il problema è che il tuo GameObject.h non ha guardie, quindi quando #include "GameObject.h" in Physics.h viene incluso quando GameObject.h include Physics.h .

Aggiungi guardie incluse in tutti i tuoi *.h intestazione *.h o *.hh (a meno che tu non abbia motivi specifici per non farlo).

Per capire cosa sta succedendo, prova ad ottenere la forma preelaborata del tuo codice sorgente. Con GCC, è qualcosa come g++ -Wall -C -E yourcode.cc > yourcode.i (non ho idea di come fanno i compilatori Microsoft). Puoi anche chiedere quali file sono inclusi, con GCC come g++ -Wall -H -c yourcode.cc

Per prima cosa è necessario includere anche le protezioni sul gameobject, ma non è questo il vero problema qui

Se qualcos’altro include physics.h, physics.h include gameobject.h, ottieni qualcosa di simile a questo:

 class GameObject { ... }; #include physics.h class Physics { ... }; 

e la #include physics.h viene scartata a causa delle guardie incluse, e si finisce con una dichiarazione di GameObject prima della dichiarazione di Physics.

Ma questo è un problema se vuoi che GameObject abbia un puntatore a una fisica, perché per la fisica htat dovrebbe essere dichiarato per primo.

Per risolvere il ciclo, puoi inoltrare una class invece, ma solo se la stai usando come puntatore o riferimento nella seguente dichiarazione, cioè:

 #ifndef PHYSICS_H #define PHYSICS_H // no need for this now #include "GameObject.h" #include  class GameObject; class Physics { private: list objects; list::iterator i; public: void ApplyPhysics(GameObject*); Vector2X CheckCollisions(Vector2X, GameObject*); }; #endif // PHYSICS_H 

Utilizzare include guardie in TUTTI i file di intestazione. Poiché utilizzi Visual Studio, puoi utilizzare #pragma once come prima definizione del preprocessore in tutte le intestazioni.

Tuttavia suggerisco di usare l’approccio classico:

 #ifndef CLASS_NAME_H_ #define CLASS_NAME_H_ // Header code here #endif //CLASS_NAME_H_ 

Seconda lettura sulla dichiarazione in avanti e applicazione.