C##包括警卫
解决了
真正帮助我的是,我可以在.cpp文件中包含头文件,而不会导致重新定义的错误。
我是C ++新手,但是我在C#和Java中有一些编程经验,所以我可能会错过C ++特有的基本东西。
问题是我真的不知道什么是错的,我会粘贴一些代码来解释这个问题。
我有三个类,GameEvents,Physics和GameObject。 我有他们每个人的标题。 GameEvents有一个Physics和一个GameObjects列表。 物理学有一个GameObjects列表。
我想要实现的是我希望GameObject能够访问或拥有一个物理对象。
如果我简单地在GameObject中包含“Physics.h”,我就会得到“错误C2111:'ClassXXX':'class'type redifinition”,据我所知。 而这正是我认为#include-guard会帮助我,所以我在我的Physics.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
但是,如果我在我的GameObject.h中包含“Physics.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(); };
我收到了很多不明白为什么会出现的问题。 如果我不#include“Physics.h”,我的代码运行得很好。
我非常感谢任何帮助。
预处理程序是一个程序,需要你的程序,做一些改变(例如包含文件(#include),宏扩展(#define),以及以#
开头的所有内容),并给编译器提供“干净”的结果。
预处理器在看到#include
时就像这样工作:
当你写:
#include "some_file"
some_file
的内容几乎可以直接拷贝到包含它的文件中。 现在如果你有:
ah: class A { int a; };
和:
bh: #include "ah" class B { int b; };
和:
main.cpp: #include "ah" #include "bh"
你得到:
main.cpp: class A { int a; }; // From #include "ah" class A { int a; }; // From #include "bh" class B { int b; }; // From #include "bh"
现在你可以看到A
是如何重新定义的。
当你写警卫时,他们会变成这样:
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
所以现在我们来看看如何扩展main中的#include
(这和前面的例子一样:copy-paste)
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
现在让我们来看看预处理器,看看这个“真正的”代码是什么。 我会一行一行地去:
// From #include "ah"
评论。 忽视! 继续:
#ifndef A_H
A_H
是否定义? 没有! 然后继续:
#define A_H
好的,现在定义了A_H
。 继续:
class A { int a; };
这不是预处理器的东西,所以就把它留下吧。 继续:
#endif
以前if
在这里完成。 继续:
// From #include "bh"
评论。 忽视! 继续:
#ifndef B_H
是否定义了B_H
? 没有! 然后继续:
#define B_H
好的,现在定义了B_H
。 继续:
#ifndef A_H // From
A_H
是否定义? 是! 然后忽略,直到相应的#endif
:
#define A_H // #include "ah"
忽视
class A { int a; }; // inside
忽视
#endif // "bh"
以前if
在这里完成。 继续:
class B { int b; };
这不是预处理器的东西,所以就把它留下吧。 继续:
#endif
以前if
在这里完成。
也就是说,在预处理器完成了文件之后,这就是编译器所看到的:
main.cpp class A { int a; }; class B { int b; };
所以,正如你所看到的,任何能够在同一个文件中包含#include
d的东西需要被直接或间接的保护。 由于.h
文件总是很有可能被包含两次,所以如果你保护所有的.h文件是很好的。
PS请注意,您也有循环#include
s。 想象一下预处理器将Physics.h的代码粘贴到GameObject.h中,该代码看到有一个#include "GameObject.h"
,这意味着将GameObject.h
复制到自身中。 当你复制的时候,你再次获得#include "Pysics.h"
,你永远陷入循环。 编译器可以防止这种情况发生,但是这意味着#include
已经完成了一半。
在说出如何解决这个问题之前,你应该知道另外一件事情。
如果你有:
#include "bh" class A { B b; };
然后编译器需要知道关于b
所有信息,最重要的是它包含了哪些变量,以便知道在A
应该放置多少字节来代替b
。
但是,如果您有:
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*
(指针)类型,所以你不需要在Physics.h
包含GameObject.h
,而只需要使用forward声明,而不是
#include "GameObject.h"
放
class GameObject;
此外,在每个头文件中放置警卫。
问题在于你的GameObject.h
没有警卫,所以当你在#include "GameObject.h"
包含Physics.h
#include "GameObject.h"
,它包含在GameObject.h
包含Physics.h
。
在所有的*.h
或*.hh
头文件中添加包含警卫(除非你有特定的理由不这样做)。
要了解正在发生的事情,请尝试获取源代码的预处理表单。 有了GCC,就像g++ -Wall -C -E yourcode.cc > yourcode.i
(我不知道微软编译器是怎么做的)。 你也可以问哪些文件包含在内,用GCC作为g++ -Wall -H -c yourcode.cc
首先,你也需要在游戏对象中加入守卫,但这不是真正的问题
如果其他东西包括physics.h首先,physics.h包含gameobject.h,你得到这样的东西:
class GameObject { ... }; #include physics.h class Physics { ... };
并且#include 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 guard。 由于您使用的是Visual Studio,因此可以使用#pragma once
作为所有标题中的第一个预处理器定义。
不过我建议使用经典的方法:
#ifndef CLASS_NAME_H_ #define CLASS_NAME_H_ // Header code here #endif //CLASS_NAME_H_
其次阅读关于前向声明并应用它。