C ++双声明或无声明问题

时间:2017-11-23 13:30:09

标签: c++

我正在写一个简单的游戏,我已经完成了,除了它有一个糟糕的文件结构,每个类只是一个.h文件。

我决定将声明和定义拆分成单独的文件,因为我遇到了循环包含问题(这是可预测的)。

现在我已将它们拆分为单独的文件,我的.h文件包含:

  • 类的声明,它的方法,枚举和其他变量。
  • 包含.cpp或.h所需的必要文件(所以基本上.cpp包含它的.h文件,它得到了所需的所有内容)。

虽然我的.cpp文件包含:

  • 方法的定义。

好的,既然你对我的文件结构有一个大概的了解,那就是我遇到的问题。

Spaceship.cpp需要Core.h中存在的cursorPos变量

Core.h包含Spaceship.h,因为它需要一个方法。

如果我在Spaceship.h中包含Core.h,我会得到双重声明。

如果我在Spaceship.h中没有包含Core.h,我就不会声明。

我很无能,我应该创建一个'Helper'类,其中包含我想在类之间交叉共享的变量吗?

修改 代码要求

Spaceship.h

#pragma once
#ifndef SPACESHIP_H
#define SPACESHIP_H

#include "Vector2D.h"
#include "Entity.h"
#include "Missile.h"
#include <SDL.h>
#include "Core.h"

extern const int SPACESHIP_SHOOT_DELAY = 50;
extern const int SPACESHIP_MAX_VELOCITY = 10;
extern const float SPACESHIP_VELOCITY_GAIN = 0.05;
extern const float SPACESHIP_VELOCITY_LOSS = -0.15;
class Spaceship : public Entity
{
    public:
        Vector2D position = Vector2D(0, 0);
        Vector2D direction = Vector2D(0, 0);
        SDL_Texture* texture;

        //Required by rendering
        SDL_Rect renderbox = { 0, 0, 32, 32 };
        SDL_Rect boundingbox = { 0, 0, 32, 32 };
        SDL_Point center = { 16, 16 };

        int lastShot = SDL_GetTicks();
        int angle = 0;
        float velocity = 0;
        void Update() override;
        void Render(SDL_Renderer* renderer) override;
        void Fire();

        Spaceship();
        ~Spaceship();
};
#endif

Core.h

#pragma once
#ifndef CORE_H
#define CORE_H

#include "Vector2D.h"
#include <SDL.h>
#include "Spaceship.h"

extern Vector2D cursorPos = Vector2D();
class Core
{
    private:
        static bool run;

    public:
        static SDL_Window* window;
        static SDL_Renderer* renderer;
        static SDL_Cursor* cursor;
        static int screenWidth;
        static int screenHeight;

        static void Initialize();
        static void ProcessEvents(SDL_Event* e);
        static void Update();
        static void Render();
        static int Execute();
        static void Cleanup();

        Core();
        ~Core();
};

#endif

我得到的错误 enter image description here

3 个答案:

答案 0 :(得分:1)

变量的定义也应该位于.cpp文件中。标题应使用extern关键字声明:

Core.h:

extern Point cursorPos;

Core.cpp:

#include "Core.h"
...
Point cursorPos;

答案 1 :(得分:0)

#pragma once作为每个头文件的第一行。这解决了多次包含的问题(即它将确保编译器不会重新包含之前已包含的包含文件的代码)

答案 2 :(得分:0)

使用全局变量是一种反模式,并且有充分的理由。然而,描述为什么人们应该避免全球状态的所有原因,是另一天的主题。在您的情况下,您在全局变量的特定实现方面遇到问题。虽然我强烈建议您重新审视您的设计,并降低全局状态的数量,但我会尝试为您遇到的问题提供解决方案和解释。

一个问题是理解C ++的包含机制,当您在C ++中包含文件时,您基本上是在代码中的那个位置插入该文件的内容。因此,如果您在头文件中声明了任何内容,则会将其声明为包含它的位置。如果多次包含文件,则会多次插入,从而多次声明变量。作为一个例子,考虑我的标题variables.h:

int someGlobalVariable = 100;

现在考虑第二个头文件:functional.h,如下所示:

#include "variables.h"

void foo(int number);

最后是一个名为functional.cpp的源文件:

#include "variables.h"
#include "functionality.h"

void foo(int number) {
    // Do something
}

这将给出编译器错误,因为我们的变量some​​GlobalVariable被定义了两次。但为什么会这样呢?如果我们尝试包含,我们将看到原因:

// variables.h
int someGlobalVariable = 100;

// functionality.h
int someGlobalVariable = 100;  // <-- included from functionality.h

void foo(int number);

void foo(int number) {
    // Do something
}

正如你所看到的那样,变量被插入了两次,而且当然不好,编译会抱怨,并且有充分的理由。 foo的前瞻声明完全合法。

我们可以在我们的头文件中添加包含警卫:

文件variables.h:

#ifndef VARIABLES_H_
#define VARIABLES_H_

int someGlobalVariable = 100;

#endif

文件功能.h:

#ifndef FUNCTIONALITY_H_
#define FUNCTIONALITY_H_
#include "variables.h"
#include "functionality.h"

void foo(int number) {
    // Do something
}

#endif

这会导致someGlobalVariable变量只包含在我们的.cpp源文件中一次。然而,这并不能使它真正具有全球性。请记住,因为C ++在我们包含文件的位置插入文件的内容,所以我们仍然会为包含它的每个源独立源文件声明一次该变量。

对于每个源模块,我们都会有一个新的实例,而不是全局的,这不是你想要的。您需要确保变量仅声明一次,然后引用。这意味着我们需要在未包含的文件中声明变量(或者最多包含一次)。让我们把它放在一个.cpp文件中。

这是一个名为globals.cpp的.cpp文件:

int someGlobalVariable.cpp = 100;

现在需要将variable.h文件修改为:

#ifndef VARIABLES_H_
#define VARIABLES_H_

extern int someGlobalVariable;

#endif

extern关键字指示链接器在另一个源单元中声明变量。因此,当我们插入包含:

时,添加包含警卫后的代码将如下所示
// variables.h
extern int someGlobalVariable;

// functionality.h
// Since VARIABLES_H_ is now defined we will not insert the contents.

void foo(int number);

void foo(int number) {
    // Do something
}

然后链接器将知道在另一个目标文件中查找someGlobalVariable的声明。最重要的是,所有引用extern int someGlobalVariable的源单元都将共享同一个实例。

在您的情况下,您应该从声明中删除=,该变量应在其自己的源单元中声明。所以你的行:

extern Vector2D cursorPos = Vector2D();

变为:

extern Vector2D cursorPos;

然后在源(.cpp)文件中声明变量如下:

Vector2D cursorPos = Vector2D();