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#includes в 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_HOk теперь
A_Hопределено. Продолжение:class A { int a; };Это не то, что нужно для препроцессора, поэтому просто оставьте это. Продолжение:
#endifПредыдущий
ifзакончился здесь. Продолжение:// From #include "b.h"Комментарий. Игнорировать! Продолжение:
#ifndef B_HОпределен ли
B_H? Нет! Затем продолжайте:#define B_HOk теперь
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; };Итак, как вы можете видеть, все, что может получить
Обратите внимание, что у вас также есть круговые#included в одном файле дважды, нужно ли охранять его прямо или косвенно. Поскольку файлы.hвсегда с большой вероятностью будут включены дважды, хорошо, если вы будете охранять все свои .H файлов.#includes. представьте себе, что препроцессор копирует-вставляет код физики.ч в объект.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_Во-вторых, прочитайте опрямом объявлении и примените его.