hashtags - C++#incluye guardias
tags for likes instagram 2017 (6)
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 compiladores de Microsoft). También puede preguntar qué archivos están incluidos, con GCC como g++ -Wall -H -c yourcode.cc
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 programació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 intento 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 <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
Pero si #incluyo "Física.h" en mi GameObject.h ahora de esta manera:
#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();
};
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 programa que toma su programa, 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 compilador.
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:
a.h:
class A { int a; };
Y:
b.h:
#include "a.h"
class B { int b; };
Y:
main.cpp:
#include "a.h"
#include "b.h"
Usted obtiene:
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"
Ahora puedes ver cómo A
se redefine.
Cuando escribes guardias, se vuelven así:
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
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 "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
Ahora sigamos al preprocesador y veamos qué código "real" surge de esto. Voy a ir línea por línea:
// From #include "a.h"
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 "b.h"
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 "a.h"
Ignorar
class A { int a; }; // inside
Ignorar
#endif // "b.h"
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 compilador:
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 compiladores 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 "b.h"
class A
{
B b;
};
Entonces el compilador 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 compilador 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 "declaración 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
}
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
.
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 declaración de GameObject antes de la declaración 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 declaración, es decir:
#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
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 declaración de reenvío, en lugar de
#include "GameObject.h"
poner
class GameObject;
Además, coloca guardias en cada archivo de encabezado.
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 declaración directa y aplicarla.