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_ 

其次阅读关于前向声明并应用它。