C++ #include 守卫

2022-01-11 00:00:00 header include c++ include-guards

已解决

真正帮助我的是我可以#include .cpp 文件中的标头而不会导致重新定义的错误.

What really helped me was that I could #include headers in the .cpp file with out causing the redefined error.

我是 C++ 新手,但我有一些 C# 和 Java 编程经验,所以我可能会遗漏一些 C++ 独有的基本知识.

I'm new to C++ but I have some programming experience in C# and Java so I could be missing something basic that's unique to C++.

问题是我真的不知道哪里出了问题,我会贴一些代码来尝试解释问题.

The problem is that I don't really know what's wrong, I will paste some code to try to explain the issue.

我有三个类,GameEvents、Physics 和 GameObject.我有他们每个人的标题.GameEvents 有一个 Physics 和一个 GameObjects 列表.Physics 有一个 GameObjects 列表.

I have three Classes, GameEvents, Physics and GameObject. I have headers for each of them. GameEvents has one Physics and a list of GameObjects. Physics has a list of GameObjects.

我想要实现的是我希望 GameObject 能够访问或拥有一个物理对象.

What I'm trying to achieve is that I want GameObject to be able to access or own a Physics object.

如果我只是在 GameObject 中 #include "Physics.h" 我会得到错误C2111:'ClassXXX':'class'类型重新定义"我理解.这就是我认为#include-guards 会有所帮助的地方,所以我在我的 Physics.h 中添加了一个包含保护,因为这是我想要包含两次的标题.

If I simply #include "Physics.h" in GameObject I get the "error C2111: 'ClassXXX' : 'class' type redifinition" which I understand. And this is where I thought #include-guards would help so I added an include guard to my Physics.h since that's the header I want to include twice.

这就是它的样子

#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 中#include "Physics.h":

But if I #include "Physics.h" in my GameObject.h now like this:

#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" 我的代码运行得很好.

I get multiple issues that don't understand why they're showing up. If I don't #include "Physics.h" my code runs just fine.

我非常感谢任何帮助.

推荐答案

预处理器是一个程序,它接受您的程序,进行一些更改(例如包含文件(#include),宏扩展(#define),基本上所有以 #) 开头,并将干净"的结果提供给编译器.

The preprocessor is a program that takes your program, makes some changes (for example include files (#include), macro expansion (#define), and basically everything that starts with #) and gives the "clean" result to the compiler.

预处理器在看到#include时是这样工作的:

The preprocessor works like this when it sees #include:

当你写作时:

#include "some_file"

some_file 的内容几乎可以直接复制粘贴到包含它的文件中.现在,如果你有:

The contents of some_file almost literally get copy pasted into the file including it. Now if you have:

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 是如何重新定义的.

Now you can see how A is redefined.

当你写守卫时,它们变成这样:

When you write guards, they become like this:

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

那么现在让我们看看 main 中的 #include 是如何被扩展的(这和之前的情况完全一样:复制粘贴)

So now let's look at how #includes in main would be expanded (this is exactly, like the previous case: 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

现在让我们跟随预处理器,看看从中产生了哪些真实"代码.我会一行一行的去:

Now let's follow the preprocessor and see what "real" code comes out of this. I will go line by line:

// From #include "a.h"

评论.忽略!继续:

#ifndef A_H

A_H 定义了吗?不!然后继续:

Is A_H defined? No! Then continue:

#define A_H

好的,现在 A_H 已定义.继续:

Ok now A_H is defined. Continue:

class A { int a; };

这不是预处理器的东西,所以不要管它.继续:

This is not something for preprocessor, so just leave it be. Continue:

#endif

之前的if到此结束.继续:

The previous if finished here. Continue:

// From #include "b.h"

评论.忽略!继续:

#ifndef B_H

B_H 定义了吗?不!然后继续:

Is B_H defined? No! Then continue:

#define B_H

好的,现在 B_H 已定义.继续:

Ok now B_H is defined. Continue:

#ifndef A_H          // From

A_H 定义了吗?是的!然后忽略直到对应的#endif:

Is A_H defined? YES! Then ignore until corresponding #endif:

#define A_H          // #include "a.h"

忽略

class A { int a; };  // inside

忽略

#endif               // "b.h"

之前的if到此结束.继续:

The previous if finished here. Continue:

class B { int b; };

这不是预处理器的东西,所以不要管它.继续:

This is not something for preprocessor, so just leave it be. Continue:

#endif

之前的if到此结束.

也就是说,在预处理器处理完文件之后,编译器看到的是这样的:

That is, after the preprocessor is done with the file, this is what the compiler sees:

main.cpp
class A { int a; };
class B { int b; };

如您所见,任何可以在同一个文件中两次获得 #included 的东西,无论是直接还是间接都需要加以保护.由于 .h 文件总是很可能被包含两次,因此最好保护所有 .h 文件.

So as you can see, anything that can get #included in the same file twice, whether directly or indirectly needs to be guarded. Since .h files are always very likely to be included twice, it is good if you guard ALL your .h files.

附:请注意,您还有循环 #include.想象一下预处理器将 Physics.h 的代码复制粘贴到 GameObject.h 中,看到有一个 #include "GameObject.h" 这意味着将 GameObject.h 复制到自身中.当你复制时,你再次得到 #include "Pysics.h" 并且你永远被困在一个循环中.编译器会阻止这种情况,但这意味着您的 #include 已经完成了一半.

P.S. Note that you also have circular #includes. Imagine the preprocessor copy-pasting the code of Physics.h into GameObject.h which sees there is an #include "GameObject.h" which means copy GameObject.h into itself. When you copy, you again get #include "Pysics.h" and you are stuck in a loop forever. Compilers prevent that, but that means your #includes are half-done.

在说如何解决这个问题之前,你应该知道另一件事.

Before saying how to fix this, you should know another thing.

如果你有:

#include "b.h"

class A
{
    B b;
};

然后编译器需要知道关于 b 的一切,最重要的是,它有哪些变量等等,以便它知道应该放置多少字节来代替 bA 中.

Then the compiler needs to know everything about b, most importantly, what variables it has etc so that it would know how many bytes it should put in place of b in A.

但是,如果您有:

class A
{
    B *b;
};

那么编译器实际上并不需要知道任何关于 B 的信息(因为指针,无论类型如何都具有相同的大小).关于B,它唯一需要知道的就是它的存在!

Then the compiler doesn't really need to know anything about B (since pointers, regardless of the type have the same size). The only thing it needs to know about B is that it exists!

所以你做了一些叫做前向声明"的事情:

So you do something called "forward declaration":

class B;  // This line just says B exists

class A
{
    B *b;
};

这与您在头文件中执行的许多其他操作非常相似,例如:

This is very similar to many other things you do in header files such as:

int function(int x);  // This is forward declaration

class A
{
public:
    void do_something(); // This is forward declaration
}

相关文章