【问题标题】:invalid use of incomplete type / forward declaration不完整类型/前向声明的无效使用
【发布时间】:2011-10-22 18:25:20
【问题描述】:

我尝试查看 Stackoverflow 和 Google 上列出的类似问题,但它们主要处理模板,这不是我的情况。我在 Debian 测试 64 位上使用 GCC 4.4.5。
所以,我有两个类 - CEntity:

#ifndef CENTITY_H_INCLUDED
#define CENTITY_H_INCLUDED

#include "global_includes.h"

// game
#include "CAnimation.h"
#include "Vars.h"
#include "vector2f.h"
#include "Utils.h"

class CAnimation;

class CEntity
{
public:
    CEntity();
    virtual ~CEntity();

    void _update(Uint32 dt);

    void updateAnimation(Uint32 dt);

    void addAnimation(const std::string& name, CAnimation* anim);
    void addAnimation(const std::string& name, const CAnimation& anim);
    void removeAnimation(const std::string& name);
    void clearAnimations();

    bool setAnimation(const std::string& name);

    SDL_Surface* getImage() const;

    const vector2f& getPos() const;
    const vector2f& getLastPos() const;
    F getX() const;
    F getY() const;
    F getLastX() const;
    F getLastY() const;
    SDL_Rect* getHitbox() const;
    SDL_Rect* getRect() const;

    F getXSpeed() const;
    F getYSpeed() const;

    void setPos(const vector2f& pos);
    void setPos(F x, F y);
    void setPos(F n);
    void setX(F x);
    void setY(F y);

    void setHitboxSize(int w, int h);
    void setHitboxSize(SDL_Rect* rect);
    void setHitboxWidth(int w);
    void setHitboxHeight(int h);

    void setSpeed(F xSpeed, F ySpeed);
    void setXSpeed(F xSpeed);
    void setYSpeed(F ySpeed);

    void stop();
    void stopX();
    void stopY();

    void affectByGravity(bool affect);

    void translate(const vector2f& offset);
    void translate(F x, F y);

    bool collide(CEntity& s);
    bool collide(CEntity* s);

protected:
    CAnimation* mCurrentAnimation;
    SDL_Surface* mImage;

    vector2f mPos;
    vector2f mLastPos;
    SDL_Rect* mHitbox; // used for collisions
    SDL_Rect* mRect; // used only for blitting

    F mXSpeed;
    F mYSpeed;

    bool mAffByGrav;

    int mHOffset;
    int mVOffset;

private:
    std::map<std::string, CAnimation*> mAnims;
};

#endif // CENTITY_H_INCLUDED

和继承自CEntity的CPlayerChar:

#ifndef CPLAYERCHAR_H_INCLUDED
#define CPLAYERCHAR_H_INCLUDED

#include "global_includes.h"

// game
#include "CEntity.h"

class CEntity;

class CPlayerChar : public CEntity
{
public:
    CPlayerChar();
    virtual ~CPlayerChar();

    virtual void update(Uint32 dt) = 0;

    virtual void runLeft() = 0;
    virtual void runRight() = 0;
    virtual void stopRunLeft() = 0;
    virtual void stopRunRight() = 0;

    virtual void attack() = 0;
    virtual void stopAttack() = 0;

    virtual void attack2() = 0;
    virtual void stopAttack2() = 0;

    virtual void ground() = 0;
    virtual void midair() = 0;

    void jump();
    void stopJump();

protected:
    // looking right?
    bool mRight;

    bool mJumping;
    bool mOnGround;
    bool mGrounded;
};

#endif // CPLAYERCHAR_H_INCLUDED

当我尝试编译它时,GCC 会抛出这个错误:

CPlayerChar.h:12: error: invalid use of incomplete type ‘struct CEntity’
CPlayerChar.h:9: error: forward declaration of ‘struct CEntity’

我首先尝试了没有前向声明'class CEntity;'在第 9 行的 CPlayerChar.h 中,但它会改为抛出这个

CPlayerChar.h:12: error: expected class-name before ‘{’ token

所以前向声明必须在那里。此外,CEntity 显然是一个类,而不是一个结构。

【问题讨论】:

  • 在 C++ 中的类和结构几乎相同;不要挂断错误消息。
  • 我认为CEntity 的类定义在文件CEntity.h 中?如果没有,那是你的问题。
  • 您的头文件中有一些循环包含。如果一个头文件也明确需要,它应该只包含其他头文件。如果您可以使用前向声明,那么这应该是首选。

标签: c++ gcc forward-declaration incomplete-type


【解决方案1】:

您的头文件中有一个循环包含。
但是如果没有所有的头文件,我们将无法修复它。

我会从这里开始。

#include "CAnimation.h"

看看你的标题,你实际上并不需要这个。您只能通过引用或指针使用 CAnimation,因此您拥有的前向声明应该足够了。将包含移动到源文件中(即从标题中移出)。

下一个我要看的地方是:

#include "global_includes.h"

任何包含在头文件中的全局包含都最好非常简单。应该只包含简单类型并且不包含任何其他头文件(除非它们同样简单)。任何复杂的事情都会导致循环依赖的问题。

一般经验法则

头文件应该只包含它绝对需要的头文件。否则它们应该包含在源文件中。如果头文件定义了一个用作父类的类,那么您绝对需要一个头文件,您拥有该类的成员对象,或者您使用该类的参数对象。

我使用术语object 来区分引用或指针。如果您使用这些,则不需要包含头文件。你只需要做一个前向声明。

【讨论】:

  • 关于循环依赖,如果他有#ifndef CANIMATION_H_INCLUDED,是不是应该防止这个问题?
  • @antogerva 这有帮助,但还不够。您还需要转发声明类型。也只包括您特别需要的标题并转发声明其他类型。
【解决方案2】:

您的包含中可能有一个循环,CPlayerChar 不知道谁是真正的 CEntity,它只知道它存在,但不知道它是什么。

如果您删除“class CEntity”声明,您将看到 GCC 会抱怨 CEntity 不存在。

您必须检查 CEntity 包含的任何内容是否包含 CPlayerChar。

【讨论】:

  • 谢谢,你的最后一行一定是它。 CEntity 包括另一个文件,其中包括 CEntity 和 CPlayerChar。现在我只需要找到一个解决方法。
【解决方案3】:

您必须确保类CEntity 的完整定义在您定义类CPlayerChar 的位置可见。 (所以请检查您的包含内容。)

这是因为您只能从完全定​​义的类继承,而不能仅从前向声明的类继承。

唯一可以用前向声明代替完整定义的情况是当你对一个类型进行指针或引用时,但前提是你从不访问它的任何成员,或者(感谢@Alf)在声明时返回类型不完整的函数。

【讨论】:

  • 前向声明比您所表明的要强大一些。例如,如果T 是一个不完整的类型,那么声明一个函数T foo() 是完全可以的。为了调用它,您需要完成T(除非T 是特殊类型void,它正式是一个永远无法完成的不完整类型),但这是一个不同的问题。 :-)
  • @Alf:谢谢!这很有用。函数参数呢,会不会不全?
猜你喜欢
  • 1970-01-01
  • 2012-06-28
  • 2014-02-02
  • 1970-01-01
  • 2019-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多