C ++ # incluye guardias

SOLUCIONADO

Lo que realmente me ayudó fue que pude #incluir encabezados en el archivo .cpp sin causar el error redefinido.


Soy nuevo en C ++, pero tengo algo de experiencia en progtwigción en C # y Java, así que podría estar perdiendo algo básico que es exclusivo de C ++.

El problema es que realmente no sé lo que está mal, pegaré un código para tratar de explicar el problema.

Tengo tres clases, GameEvents, Physics y GameObject. Tengo encabezados para cada uno de ellos. GameEvents tiene una Física y una lista de GameObjects. Física tiene una lista de GameObjects.

Lo que bash lograr es que quiera que GameObject pueda acceder o ser dueño de un objeto de Física.

Si simplemente # incluyo “Physics.h” en GameObject obtengo el “error C2111: ‘ClassXXX’: ‘clase’ de tipo redifinition” que entiendo. Y aquí es donde pensé que # incluir-guardias ayudaría, así que agregué un protector de inclusión a mi Physics.h ya que ese es el encabezado que quiero incluir dos veces.

Así es como se ve

#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 

Pero si #incluyo “Física.h” en mi GameObject.h ahora de esta manera:

 #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(); }; 

Recibo múltiples problemas que no entienden por qué aparecen. Si no # incluye “Física.h” mi código funciona bien.

Estoy muy agradecido por cualquier ayuda.

El preprocesador es un progtwig que toma su progtwig, realiza algunos cambios (por ejemplo, incluye archivos (#include), expansión de macros (#define) y básicamente todo lo que comienza con # ) y le da el resultado “limpio” al comstackdor.

El preprocesador funciona así cuando ve #include :

Cuando escribes:

 #include "some_file" 

El contenido de some_file literalmente casi literalmente el archivo que lo incluye. Ahora si tienes:

 ah: class A { int a; }; 

Y:

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

Y:

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

Usted obtiene:

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

Ahora puedes ver cómo A se redefine.

Cuando escribes guardias, se vuelven así:

 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 

Entonces, veamos cómo se expandiría #include s en main (esto es exactamente, como en el caso anterior: copiar y pegar)

 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 

Ahora sigamos al preprocesador y veamos qué código “real” surge de esto. Voy a ir línea por línea:

 // From #include "ah" 

Comentario. ¡Ignorar! Continuar:

 #ifndef A_H 

¿ A_H definido A_H ? ¡No! Luego continúa:

 #define A_H 

Ok, ahora A_H está definido. Continuar:

 class A { int a; }; 

Esto no es algo para el preprocesador, así que déjalo. Continuar:

 #endif 

El anterior if terminó aquí. Continuar:

 // From #include "bh" 

Comentario. ¡Ignorar! Continuar:

 #ifndef B_H 

¿Está B_H definido? ¡No! Luego continúa:

 #define B_H 

Ok, ahora B_H está definido. Continuar:

 #ifndef A_H // From 

¿ A_H definido A_H ? ¡SÍ! Luego ignore hasta el correspondiente #endif :

 #define A_H // #include "ah" 

Ignorar

 class A { int a; }; // inside 

Ignorar

 #endif // "bh" 

El anterior if terminó aquí. Continuar:

 class B { int b; }; 

Esto no es algo para el preprocesador, así que déjalo. Continuar:

 #endif 

El anterior if terminó aquí.

Es decir, después de que el preprocesador haya terminado con el archivo, esto es lo que ve el comstackdor:

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

Entonces, como puede ver, cualquier cosa que pueda obtener #include d en el mismo archivo dos veces, ya sea directa o indirectamente, debe ser protegida. Como es muy probable que los archivos .h se incluyan dos veces, es bueno que proteja TODOS sus archivos .h.

PS Tenga en cuenta que también tiene #include s circular. Imagine el preprocesador copiando y pegando el código de Physics.h en GameObject.h que ve que hay un #include "GameObject.h" que significa copiar GameObject.h en sí mismo. Cuando copias, nuevamente obtienes #include "Pysics.h" y estás atrapado en un ciclo para siempre. Los comstackdores lo evitan, pero eso significa que sus #include están medio hechos.

Antes de decir cómo arreglar esto, debes saber otra cosa.

Si usted tiene:

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

Entonces el comstackdor necesita saber todo sobre b , lo que es más importante, qué variables tiene, etc. para saber cuántos bytes debería poner en lugar de b en A

Sin embargo, si tiene:

 class A { B *b; }; 

Entonces, el comstackdor realmente no necesita saber nada sobre B (ya que los punteros, independientemente del tipo, tienen el mismo tamaño). ¡Lo único que necesita saber sobre B es que existe!

Entonces haces algo llamado “statement adelantada”:

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

Esto es muy similar a muchas otras cosas que hace en archivos de encabezado como:

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

Tiene referencias circulares aquí: Physics.h incluye GameObject.h que incluye Physics.h . La clase de Physics utiliza el tipo GameObject* (puntero), por lo que no es necesario que incluya GameObject.h en Physics.h sino que solo use la statement de reenvío, en lugar de

 #include "GameObject.h" 

poner

 class GameObject; 

Además, coloca guardias en cada archivo de encabezado.

El problema es que su GameObject.h no tiene guardias, por lo que cuando #include "GameObject.h" en Physics.h , se incluye cuando GameObject.h incluye Physics.h .

Agregue incluir guardias en todos sus archivos de encabezado *.h o *.hh (a menos que tenga razones específicas para no hacerlo).

Para comprender lo que está sucediendo, intente obtener la forma preprocesada de su código fuente. Con GCC, es algo así como g++ -Wall -C -E yourcode.cc > yourcode.i (no tengo idea de cómo lo hacen los comstackdores de Microsoft). También puede preguntar qué archivos están incluidos, con GCC como g++ -Wall -H -c yourcode.cc

En primer lugar, debes incluir guardias en gameobject también, pero ese no es el problema real aquí

Si algo más incluye física.h primero, physics.h incluye gameobject.h, obtienes algo como esto:

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

y el #include physics.h se descarta debido a los guardias de inclusión, y terminas con una statement de GameObject antes de la statement de Física.

Pero eso es un problema si quieres que GameObject tenga un puntero a Física, porque para eso la física debería declararse primero.

Para resolver el ciclo, puede reenviar-declarar una clase en su lugar, pero solo si solo lo está usando como un puntero o una referencia en la siguiente statement, es decir:

 #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 

Utilice incluir guardias en TODOS sus archivos de encabezado. Como está utilizando Visual Studio, podría usar #pragma once como la primera definición de preprocesador en todos sus encabezados.

Sin embargo, sugiero usar el enfoque clásico:

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

Segunda lectura sobre la statement directa y aplicarla.