C++ #включить охрану
Решено
Что действительно помогло мне, так это то, что я мог #включать заголовки в .cpp-файле, что вызывает пересмотрел ошибки.
Я новичок в C++, но у меня есть некоторый опыт программирования на C# и Java, поэтому я могу упустить что-то основное, что уникально для C++.
Проблема в том, что я действительно не знаю, что не так, я вставлю некоторый код, чтобы попытаться объяснить проблему.
У меня есть три класса: GameEvents, Physics и GameObject. У меня есть заголовки для каждого из них. их. GameEvents имеет одну физику и список GameObjects. Физика имеет список игровых объектов.То, что я пытаюсь добиться, это то, что я хочу, чтобы объект, чтобы быть в состоянии получить доступ или собственный объект физики.
Если я просто #включаю " физику.h " в GameObject я получаю "ошибка C2111: 'ClassXXX' : 'переопределенности тип класса", которые я понимаю. И вот тут я подумал, что #include-guards поможет, поэтому я добавил include guard в свою физику.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
Но если я #включаю " физику.h " в моем игровом объекте.h теперь вот так:
#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();
};
Я получаю множество вопросов, которые не понимают, почему они появляются. Если я не включу " физику.h " мой код работает просто отлично.
Я очень благодарен за любую помощь.
6 ответов:
Препроцессор-это программа, которая берет вашу программу, вносит некоторые изменения (например, включает файлы (#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
#include
s в main будет расширяться (это точно, как и в предыдущем случае: copy-paste)Теперь давайте проследим за препроцессором и посмотрим, какой "реальный" код получается из этого. Я буду идти строка за строкой: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; };
Итак, как вы можете видеть, все, что может получить
Обратите внимание, что у вас также есть круговые#include
d в одном файле дважды, нужно ли охранять его прямо или косвенно. Поскольку файлы.h
всегда с большой вероятностью будут включены дважды, хорошо, если вы будете охранять все свои .H файлов.#include
s. представьте себе, что препроцессор копирует-вставляет код физики.ч в объект.h, который видит, есть#include "GameObject.h"
, что означает копированиеGameObject.h
в себя. Когда вы копируете, вы снова получаете#include "Pysics.h"
, и вы застряли в петле навсегда. Компиляторы предотвращают это, но это означает, что ваши#include
наполовину сделано. Прежде чем сказать, как это исправить, вы должны знать еще одну вещь.Если у вас есть:
#include "b.h" class A { B b; };
Тогда компилятор должен знать все о
b
, Самое главное, какие переменные он имеет и т. д., чтобы он знал, сколько байт он должен поставить вместоb
вA
.Однако, если у вас есть:
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 }
У вас есть круговые ссылки здесь:
Physics.h
включаетGameObject.h
который включаетPhysics.h
. Ваш классPhysics
использует типGameObject*
(указатель), поэтому вам не нужно включатьGameObject.h
вPhysics.h
, а просто использовать прямое объявление-вместо#include "GameObject.h"
Put
class GameObject;
Кроме того, поставьте охрану в каждом заголовочном файле.
Проблема в том, что ваш
GameObject.h
не имеет охранников, поэтому когда вы#include "GameObject.h"
вPhysics.h
он включается, когдаGameObject.h
включаетPhysics.h
.
Добавьте include guards во все ваши заголовочные файлы
Чтобы понять, что происходит, попробуйте получить предварительно обработанную форму исходного кода. С GCC это что-то вроде*.h
или*.hh
(Если у вас нет особых причин этого не делать).g++ -Wall -C -E yourcode.cc > yourcode.i
(я понятия не имею, как это делают компиляторы Microsoft). Вы также можете спросить, какие файлы включены, с GCC какg++ -Wall -H -c yourcode.cc
Во-первых, вам нужно включить охрану и на gameobject, но это не настоящая проблема здесь
Если что-то еще включает физику.во-первых, физика.h включает в себя gameobject.h, вы получите что-то вроде этого:
class GameObject { ... }; #include physics.h class Physics { ... };
И #включают физику.h отбрасывается из-за включения охранников, и вы заканчиваете с объявлением GameObject перед объявлением физики.
Но это проблема, если вы хотите, чтобы GameObject имел указатель на физику, потому что для htat физики сначала нужно было бы объявить об этом.
Чтобы разрешить цикл, вы можете объявить класс вперед, но только если вы просто используете его в качестве указателя или ссылки в следующем объявлении, т. е.:
#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
Используйте include guards в всех ваших заголовочных файлах. Поскольку вы используете Visual Studio, вы можете использовать
#pragma once
в качестве первого определения препроцессора во всех заголовках.Однако я предлагаю использовать классический подход:
#ifndef CLASS_NAME_H_ #define CLASS_NAME_H_ // Header code here #endif //CLASS_NAME_H_
Во-вторых, прочитайте опрямом объявлении и примените его.