我有三门课,GameEvents,Physics和GameObject。我有他们每个人的标题。GameEvents有一个Physics和一个GameObjects列表。物理学有一个游戏对象列表。
我试图实现的GameObject能够访问或拥有一个物理对象。
如果我在GameObject中简单地使用#include "Physics.h"
,我会得到我理解的error C2111: 'ClassXXX' : 'class' type redifinition
。这就是我认为包含守卫会有帮助的地方,所以我在我的物理中添加了一个包含守卫。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”我的代码运行得很好。
8条答案
按热度按时间gmxoilav1#
预处理器是一个程序,它接受你的程序,做一些修改(例如包含文件(#include),宏扩展(#define),以及基本上以
#
开头的所有内容),并将“干净”的结果提供给编译器。当预处理器看到
#include
时,它的工作原理是这样的:当你写:
some_file
的内容几乎是从字面上复制粘贴到包含它的文件中。现在,如果您有:并且:
并且:
您将获得:
现在您可以看到
A
是如何重新定义的。当你编写guards时,它们会变成这样:
现在让我们看看main中的
#include
s是如何扩展的(这和前面的例子完全一样:复制粘贴)现在让我们跟随预处理器,看看从中产生了什么“真实的的”代码。我会一行一行地说:
评论。忽略!继续:
是否定义了
A_H
?不!那就继续:现在,
A_H
被定义了。继续:这不是预处理器的东西,所以不要管它。继续:
上一个
if
在这里完成。继续:评论。忽略!继续:
是否定义了
B_H
?不!那就继续:现在,
B_H
被定义了。继续:是否定义了
A_H
?是!然后忽略,直到对应的#endif
:忽略
忽略
之前的
if
在这里结束。继续:这不是预处理器的东西,所以不要管它。继续:
上一个
if
在这里完成。也就是说,在预处理器处理完文件后,编译器看到的是:
因此,正如您所看到的,任何可以在同一个文件中获得
#include
d两次的东西,无论是直接还是间接,都需要加以保护。由于.h
文件总是很有可能被包括两次,所以如果您保护所有的。h文件。P.S.注意你也有圆形的
#include
s。想象一下,预处理器复制粘贴物理代码。h进入GameObject。h看到有一个#include "GameObject.h"
,这意味着将GameObject.h
复制到它自己。当你复制时,你再次得到#include "Pysics.h"
,你永远陷入了一个循环。编译器可以防止这种情况,但这意味着#include
已经完成了一半。在说如何解决这个问题之前,你应该知道另一件事。
如果您有:
然后编译器需要知道关于
b
的一切,最重要的是,它有什么变量等,这样它就知道应该在A
中放置多少字节来代替b
。但是,如果您有:
这样编译器就不需要知道任何关于
B
的信息(因为指针,不管是什么类型,都有相同的大小)。关于B
,它唯一需要知道的就是它的存在!所以你做了一个叫做“向前声明”的事情:
这与您在头文件中执行的许多其他操作非常相似,例如:
kupeojn62#
您在此处有循环引用:
Physics.h
包括GameObject.h
,GameObject.h
包括Physics.h
。您的类Physics
使用GameObject*
(指针)类型,因此您不需要在Physics.h
中包含GameObject.h
,而只需使用向前声明-而不是放
此外,在每个头文件中放置防护。
rekjcdws3#
问题是你的
GameObject.h
没有保护,所以当你在Physics.h
中使用#include "GameObject.h"
时,当GameObject.h
包含Physics.h
时,它会被包含。5lhxktic4#
在所有
*.h
或*.hh
头文件中添加include guards(除非您有特殊原因不这样做)。要了解发生了什么,请尝试获取源代码的预处理形式。对于GCC,它类似于
g++ -Wall -C -E yourcode.cc > yourcode.i
(我不知道Microsoft编译器是如何做到这一点的)。您还可以询问包含哪些文件,GCC为g++ -Wall -H -c yourcode.cc
vmpqdwk35#
首先,你需要在gameobject上包含守卫,但这不是真实的的问题
如果其他东西包括物理学。h首先,物理。h包括游戏对象。h,你会得到这样的结果:
包括物理学。h由于include guards而被丢弃,并且您最终在Physics声明之前声明了GameObject。
但是如果你想让GameObject有一个指向物理的指针,这就有问题了,因为对于htat物理必须首先声明。
要解决这个循环,你可以改为前向声明一个类,但前提是你只是在下面的声明中将它用作指针或引用。即:
56lgkhnf6#
在头文件中使用include guards。因为你使用的是Visual Studio,所以你可以使用
#pragma once
作为所有头文件中的第一个预处理器定义。但是我建议使用经典的方法:
第二次阅读向前声明并应用它。
c6ubokkw7#
头保护的目的是避免多次包含同一个文件。但是目前在C++中使用的头保护是可以改进的。目前的守卫是:
我的新警卫提议是:
这解决了当AAA类需要BBB类声明,而BBB类需要AAA类声明时发生的恼人问题,通常是因为从一个类到另一个类存在交叉指针:
我希望这一点能够包含在自动从模板生成代码的IDE中。
knpiaxh18#
“#pragma once“::其作用与报头保护相同,并且具有更短和更不易出错的附加优点。
许多编译器使用#pragma指令支持更简单的头保护的替代形式:“#pragma once“//你的代码在这里
然而,#pragma once并不是 C++ 的正式组成部分,也不是所有的编译器都支持它(尽管大多数现代编译器都支持)。
出于兼容性的考虑,人们建议坚持使用传统的头罩。它们并不需要太多的工作,并且保证在所有兼容的编译器上都支持它们。