【问题标题】:Inheritance error dilemma: "invalid use of incomplete type" VS "expected class-name"继承错误困境:“不完整类型的无效使用”VS“预期的类名”
【发布时间】:2023-11-13 05:06:01
【问题描述】:

所以我试图让“Herder”类从“Mob”类继承。但我收到如下编译器错误:

error: invalid use of incomplete type 'struct Mob'
error: forward declaration of 'struct Mob'

这就是 Herder.h 的样子:

#ifndef HERDER_H_INCLUDED
#define HERDER_H_INCLUDED

#include <SFML/Graphics.hpp>
#include <cstdlib>
#include "Level.h"
#include "Mob.h"

class Mob;

class Herder : public Mob
{
public:
    //Member functions.
    Herder(Level* level, int x, int y);
    void virtual GameCycle(sf::RenderWindow* App);
    void virtual Die();
    void Roar();
protected:
    float m_RoarCountdown;
    float m_RoarTime;
    float m_Speed;
    bool m_Roaring;
};

#endif // HERDER_H_INCLUDED

确定它一定是导致此问题的class Mob;,我将其删除,但随后出现以下错误,指的是花括号打开的行:

error: expected class-name before '{' token

这实际上是我最初添加前向声明的原因 - 我曾认为编译器无法识别 class Herder : public Mob 中的 Mob,所以我想我会前向声明。

我不认为这是周期性依赖的情况,就像我通过 Google 发现的某些情况一样 - “Mob.h” 与 Herder 类无关。

我已尝试完全删除 #include "Mob.h" 并仅使用前向声明,但这也不起作用 - 我再次收到一个错误:

error: invalid use of incomplete type 'struct Mob'

这令人困惑。我之前已经成功地获得了要继承的类,并且这段代码在所有相关方面都与我之前的成功尝试类似。


编辑:这是Mob.h的内容

#ifndef MOB_H_INCLUDED
#define MOB_H_INCLUDED

#include <SFML/Graphics.hpp>
#include <cstdlib>
#include "Level.h"

class Level;

class Mob
{
public:
    //Member functions.
    Mob(Level* level, int x, int y);
    float GetX();
    float GetY();
    void SetColor(sf::Color color);
    void virtual GameCycle(sf::RenderWindow* App) = 0;
    void virtual Die() = 0;
    void Draw(sf::RenderWindow* App);
protected:
    float m_X;
    float m_Y;
    bool m_Moving;
    int m_Health;
    sf::Sprite m_Sprite;
    Level* pLevel;
};

#endif // MOB_H_INCLUDED

编辑:这里是“Level.h”文件的内容。请注意BabyMob 的子类,其方式与Herder 大致相同;两者都遇到相同的错误。

#ifndef LEVEL_H_INCLUDED
#define LEVEL_H_INCLUDED

#include <SFML/Graphics.hpp>
#include <cstdlib>
#include "Tile.h"
#include "Herder.h"
#include "Baby.h"

class Tile;
class Herder;
class Baby;

/// LEVEL
/// This is the collection of all data regarding a level, including layout, objects, mobs, and story elements.
///

class Level
{
public:
    //Constructor
    Level(int height, int width, std::string name);
    //For obtaining positional data
    int GetHeight();
    int GetWidth();
    std::string GetName();
    sf::Image GetTileImage(int image);
    sf::Image GetMobImage(int image);
    std::vector< std::vector<Tile> >& GetGrid();
    void NewHerder(int x, int y);
    void NewBaby(int x, int y);
    void GameCycle(sf::RenderWindow* App);
    void GraphicsCycle(sf::RenderWindow* App);
private:
    //Size
    int m_Height;
    int m_Width;
    //Spatial coords
    std::string m_Name;
    //The grid of tiles.
    std::vector< std::vector<Tile> > m_Grid;
    //A vector of the images to be used for tiles.
    std::vector<sf::Image> m_TileImages;
    //A vector of the images to be used for tiles.
    std::vector<sf::Image> m_MobImages;
    //The herders
    std::vector<Herder> m_Herders;
    //The babies
    std::vector<Baby> m_Babies;
};

#endif // LEVEL_H_INCLUDED

编辑:先发制人,这里是Tile.h的内容:

#ifndef TILE_H_INCLUDED
#define TILE_H_INCLUDED

#include <SFML/Graphics.hpp>
#include <cstdlib>
#include "Level.h"

class Level;

/// TILE
/// This is the basic environmental unit in the game
///

class Tile
{
public:
    //Constructor
    Tile(int col, int row, int size, int type, Level* level);
    //For obtaining positional data
    int GetX();
    int GetY();
    int GetRow();
    int GetCol();
    //For obtaining type data
    int GetType();
    //For obtaining string type data
    std::string GetStringType();
    //For changing type data
    void SetType(int type);
    void SetStringType(std::string character);
    //For activities that regularly take place
    void GameCycle();
    //Draws the tile.
    void Draw(sf::RenderWindow* App);
private:
    //The level this tile belongs to.
    Level* m_Level;
    //Size (it's a square!)
    int m_Size;
    //Spatial coords
    int m_X;
    int m_Y;
    //Grid coords
    int m_Row;
    int m_Col;
    //Type
    int m_Type;
    //Visual data
    sf::Sprite m_Tile;
};

#endif // TILE_H_INCLUDED

【问题讨论】:

  • 请贴出 mob.h 的内容。我认为问题就在那里。此外,class Mob 不是必需的,您在 mob.h 中定义它。
  • Mob.h 包含什么?如果它包含class Mob,那么您应该不会收到任何错误。
  • 确定 mob.h 有一个 unique 包含守卫吗?就好像它根本不包括在内。那里的类​​名没有拼写错误?
  • 更新了! Mob.h 现在在那里。
  • 如果您仍然要包含这些文件,请删除所有前向声明...

标签: c++ inheritance forward-declaration


【解决方案1】:

是一个循环依赖(Herder.h包含Level.h,Level.h又包含Herder.h等)。

在 Herder.h 中,只需替换:

#include "Level.h"

与:

class Level;

在 Mob.h 中做同样的事情

一般规则是包含尽可能少的头文件(即仅包含您需要的头文件)。如果您可以使用前向声明,例如,那么使用它而不是完整的包含。

【讨论】:

    【解决方案2】:

    您遇到的问题是循环依赖,这是一种代码异味。一方面,为了能够从一个类型派生,基本定义必须对编译器可用(即编译器需要一个完全定义的类型来继承)。另一方面,您的基类依赖派生类。

    技术上的答案是前向声明派生类型(以便您可以定义基类),然后定义基类。但是你真的应该想想你在做什么:为什么这两种不同的类型通过继承相关联?为什么没有一个?还是三个(职责分工)?如果基础依赖于它自己的接口的派生接口,这似乎表明它们耦合度太高。重新考虑设计。

    【讨论】:

      【解决方案3】:

      "Herder.h""Level.h"#include。所以,我认为这个错误来自首先包含的"Herder.h"。它正在变得循环。删除它,看看错误是否消失。

      【讨论】:

        最近更新 更多