C++ #include 守卫

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8020113/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:52:33  来源:igfitidea点击:

C++ #include guards

c++headerincludeinclude-guards

提问by Orujimaru

SOLVED

解决了

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

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



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++.

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

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

问题是我真的不知道出了什么问题,我会粘贴一些代码来尝试解释问题。

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.

我有三个类,GameEvents、Physics 和 GameObject。我有他们每个人的标题。GameEvents 有一个 Physics 和一个 GameObjects 列表。物理学有一个游戏对象列表。

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

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

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.

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

This is how it looks

这是它的样子

#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

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

但是如果我在 GameObject.h 中 #include "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();
};

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

I'm very grateful for any help.

我非常感谢任何帮助。

回答by Shahbaz

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)、宏扩展(#define)以及基本上以 开头的所有内容#)并将“干净”的结果提供给编译器。

The preprocessor works like this when it sees #include:

预处理器在看到时的工作方式如下#include

When you write:

当你写:

#include "some_file"

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

some_file几乎字面上的内容被复制粘贴到包含它的文件中。现在如果你有:

a.h:
class A { int a; };

And:

和:

b.h:
#include "a.h"
class B { int b; };

And:

和:

main.cpp:
#include "a.h"
#include "b.h"

You get:

你得到:

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"

Now you can see how Ais redefined.

现在您可以看到如何A重新定义。

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

So now let's look at how #includes in main would be expanded (this is exactly, like the previous case: copy-paste)

那么现在让我们看看#includemain 中的 s 将如何扩展(这正是,就像之前的情况:复制粘贴)

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"

Comment. Ignore! Continue:

评论。忽略!继续:

#ifndef A_H

Is A_Hdefined? No! Then continue:

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

#define A_H

Ok now A_His defined. Continue:

好的,现在A_H定义了。继续:

class A { int a; };

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

这不是预处理器的东西,所以就让它吧。继续:

#endif

The previous iffinished here. Continue:

上一个if到此结束。继续:

// From #include "b.h"

Comment. Ignore! Continue:

评论。忽略!继续:

#ifndef B_H

Is B_Hdefined? No! Then continue:

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

#define B_H

Ok now B_His defined. Continue:

好的,现在B_H定义了。继续:

#ifndef A_H          // From

Is A_Hdefined? YES! Then ignore until corresponding #endif:

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

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

Ignore

忽略

class A { int a; };  // inside

Ignore

忽略

#endif               // "b.h"

The previous iffinished here. Continue:

上一个if到此结束。继续:

class B { int b; };

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

这不是预处理器的东西,所以就让它吧。继续:

#endif

The previous iffinished here.

上一个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; };

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

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

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.hinto 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.

PS 请注意,您也有圆形#includes。想象一下预处理器将 Physics.h 的代码复制粘贴到 GameObject.h 中,它发现存在一个#include "GameObject.h"意味着复制GameObject.h到自身中。当你复制时,你又得到了#include "Pysics.h",你永远陷入了一个循环。编译器会阻止这种情况,但这意味着您的#includes 已完成一半。

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

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

If you have:

如果你有:

#include "b.h"

class A
{
    B b;
};

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 bin A.

那么编译器需要知道的一切b,最重要的是,它有哪些变量等,以便它知道它应该有多少字节落实到位bA

However, if you have:

但是,如果您有:

class A
{
    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 Bis that it exists!

然后编译器真的不需要知道任何关于B(因为指针,无论类型如何都具有相同的大小)。它唯一需要知道的B是它存在!

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
}

回答by Bojan Komazec

You have circular references here: Physics.hincludes GameObject.hwhich includes Physics.h. Your class Physicsuses GameObject*(pointer) type so you don't need to include GameObject.hin Physics.hbut just use forward declaration - instead of

您在这里有循环引用:Physics.h包括GameObject.h其中包括Physics.h。您的类Physics使用GameObject*(pointer) 类型,因此您不需要包含GameObject.h在其中,Physics.h而只需使用前向声明 - 而不是

#include "GameObject.h" 

put

class GameObject;   

Furthermore, put guards in each header file.

此外,在每个头文件中放置保护。

回答by je4d

Firstly you need include guards on gameobject too, but that's not the real problem here

首先,您还需要在游戏对象上包含守卫,但这不是真正的问题

If something else includes physics.h first, physics.h includes gameobject.h, you get something like this:

如果其他东西首先包含physics.h,physics.h包含gameobject.h,你会得到这样的东西:

class GameObject {
...
};

#include physics.h

class Physics {
...
};

and the #include physics.h gets discarded because of the include guards, and you end up with a declaration of GameObject before the declaration of Physics.

并且#includephysics.h 由于包含守卫而被丢弃,并且您最终在声明 Physics 之前声明了 GameObject。

But that's a problem if you want GameObject to have a pointer to a Physics, because for htat physics would have to be declared first.

但是如果你想让 GameObject 有一个指向 Physics 的指针,这是一个问题,因为对于 htat Physics 必须首先声明。

To resolve the cycle, you can forward-declare a class instead, but only if you are just using it as a pointer or a reference in the declaration following, i.e.:

要解决循环问题,您可以改为向前声明一个类,但前提是您仅将其用作以下声明中的指针或引用,即:

#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

回答by bitmask

The issue is that your GameObject.hdoes not have guards, so when you #include "GameObject.h"in Physics.hit gets included when GameObject.hincludes Physics.h.

问题是你GameObject.h没有守卫,所以当你#include "GameObject.h"Physics.h它被包含时GameObject.hcontains Physics.h

回答by Basile Starynkevitch

Add include guards in all your *.hor *.hhheader files (unless you have specific reasons to not do that).

在您的所有文件*.h*.hh头文件中添加包含保护(除非您有特定的理由不这样做)。

To understand what is happening, try to get the preprocessed form of your source code. With GCC, it is something like g++ -Wall -C -E yourcode.cc > yourcode.i(I have no idea on how Microsoft compilers do that). You can also ask which files are included, with GCC as g++ -Wall -H -c yourcode.cc

要了解发生了什么,请尝试获取源代码的预处理形式。使用 GCC,它类似于g++ -Wall -C -E yourcode.cc > yourcode.i(我不知道 Microsoft 编译器如何做到这一点)。您还可以询问包含哪些文件,使用 GCC 作为g++ -Wall -H -c yourcode.cc

回答by pnezis

Use include guards in ALLyour header files. Since you are using Visual Studio you could use the #pragma onceas the first preprocessor definition in all your headers.

所有头文件中使用包含保护。由于您使用的是 Visual Studio,因此您可以将#pragma once用作所有标头中的第一个预处理器定义。

However I suggest to use the classical approach:

但是我建议使用经典方法:

#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_

// Header code here

#endif //CLASS_NAME_H_

Second read about forward declarationand apply it.

第二次阅读前向声明并应用它。

回答by angarciaba

The goal of a header guard is to avoid including the same file many times. But the header guard that is currently used in C ++ can be improved. The current guard is:

头部保护的目标是避免多次包含同一个文件。但是目前在C++中使用的header guard可以改进。目前的后卫是:

#ifndef AAA_H
#define AAA_H

class AAA
{ /* ... */ };

#endif

My new guard proposal is:

我的新警卫提议是:

#ifndef AAA_H
#define AAA_H

class AAA
{ /* ... */ };

#else
class AAA;  // Forward declaration
#endif

This resolves the annoying problem that occurs when the AAA class needs the BBB class declaration, while the BBB class needs the AAA class declaration, typically because there are crossed pointers from one class to the other:

这解决了当 AAA 类需要 BBB 类声明,而 BBB 类需要 AAA 类声明时出现的恼人问题,通常是因为从一个类到另一个类存在交叉指针:

// File AAA.h
#ifndef AAA_H
#define AAA_H

#include "BBB.h"

class AAA
{ 
  BBB *bbb;
/* ... */ 
};

#else
class AAA;  // Forward declaration
#endif

//+++++++++++++++++++++++++++++++++++++++

// File BBB.h
#ifndef BBB_H
#define BBB_H

#include "AAA.h"

class BBB
{ 
  AAA *aaa;
/* ... */ 
};

#else
class BBB;  // Forward declaration
#endif

I would love for this to be included in the IDEs that automatically generate code from templates.

我希望将其包含在自动从模板生成代码的 IDE 中。

回答by Ramanand Yadav

" #pragma once " ::: serves the same purpose as header guards, and has the added benefit of being shorter and less error-prone.

" #pragma once " ::: 与标题守卫的目的相同,并且具有更短且不易出错的额外好处。

Many compilers support a simpler, alternate form of header guards using the #pragma directive: " #pragma once " // your code here

许多编译器使用 #pragma 指令支持更简单的替代形式的头文件保护:“ #pragma once ” // 你的代码在这里

However, #pragma once is not an official part of the C++ language, and not all compilers support it (although most modern compilers do).

然而,#pragma once 不是 C++ 语言的官方部分,并非所有编译器都支持它(尽管大多数现代编译器都支持)。

For compatibility purposes, people recommend sticking to traditional header guards. They aren't much more work and they're guaranteed to be supported on all compliant compilers.

出于兼容性目的,人们建议坚持使用传统的头部防护。它们的工作量并不多,并且保证所有兼容的编译器都支持它们。