为什么我需要转发声明 class foo 而不是 class bar,即使 'foo.h' 和 'bar.h' 都包含在内?

Why do I need to forward declare class foo but not class bar, even though 'foo.h' and 'bar.h' are both included?

我在尝试编译此代码时收到 C2143 错误;我通过前向声明 class Level 修复了它,但我对为什么需要这样做感到困惑,因为我已经包含了 #include Level.h,这当然是定义 Level 的地方。不过,更让我困惑的是,我 似乎不需要 对同一个地方的类似陈述采取相同的措施。这是我的代码:

// global.h
// Global variables

#pragma once

#include "Level.h"
#include "view.h"
#include "ActorPlayer.h"

class Level; // Without this line C2143 (missing ; before *) is
             // generated after 'extern Level'

extern Level* gamelevel;
extern View* view;
extern bool gameover;
extern bool player_turn;
extern int game_ticks;
extern ActorPlayer* act_player;

我的问题是,为什么我不需要 class View;class ActorPlayer;?相关区别是什么?

万一重要,这里是Level.h:

// Level.h
// Header file for Level class

#pragma once
#include "Terrain.h"
#include "global.h"
#include "hex_math.h"

class Level
{
public:
    const int WIDTH;
    const int HEIGHT;

    Level();
    Level(int width, int height);
    Level(int radius);
    ~Level();

    bool isInBounds(OrdPair coord);

    int terrainAt(OrdPair coord);

    void update();

private:
    int** terrain;

    void init();

    void fillHex(int qCenter, int rCenter, int radius, int terrainID);

};

问题是您对包含文件存在循环依赖:

global.h includes level.h
level.h includes global.h

发生的事情是

  1. 编译器开始读取level.h
  2. 读取 pragma once 将文件标记为已包含
  3. 找到 global.h 并开始阅读(此时)
  4. 读取 pragma once 将文件标记为已包含
  5. 发现包含 level.h 但忽略它(它被标记为包含在 2 中)
  6. 继续阅读 global.h 的其余部分,但 class Level 未知

如果项目不是微不足道的,你应该制作一个你的 classes 和模块的图表,并决定什么依赖于(建立在)什么上。这个图应该是一个 DAG(没有循环)。更具体地说,应该可以细分 "layers" 中的模块,其中较低层的模块不依赖于较高层中的模块。

如果你的依赖关系图中有一个循环,项目将更难处理(例如单元测试),因为你的模块不是真正的模块,而是整个程序变成了一个巨大的代码球。

在 global.h 中包含 Level.h,但在 Level.h 中包含 global.h。这个问题很可能是由于循环依赖造成的。

#pragma once的思想其实并不是为了防止循环依赖,而是为了防止一个文件被包含两次。

#pragma once 在您的特定情况下是否有效实际上取决于您使用的编译器。

最简单的解决方案是完全避免循环依赖。