web-dev-qa-db-ja.com

不完全な型の無効な使用/前方宣言

StackoverflowとGoogleでここにリストされている同様の問題を確認しようとしましたが、それらは主にテンプレートを処理しており、それは私のケースではありません。 Debian Testing 64ビットでGCC 4.4.5を使用しています。
だから、私は2つのクラスを持っています-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’

最初に、「CEntityクラス」という前方宣言なしで試してみました。 9行目のCPlayerChar.hにありますが、代わりにこれをスローします

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

したがって、フォワード宣言はそこになければなりません。また、CEntityは明らかにクラスではなく、構造体です。

15
rivon

ヘッダーファイルに循環インクルードがあります。
しかし、すべてのヘッダーファイルがないと、修正できません。

ここから始めます。

#include "CAnimation.h"

ヘッダーを見ると、実際には必要ありません。参照またはポインタによってのみCAnimationを使用するため、取得した前方宣言で十分です。インクルードをソースファイルに(つまり、ヘッダーから)移動します。

次に見るのは次の場所です。

#include "global_includes.h"

ヘッダーファイルに含まれるグローバルインクルードは、非常に単純である方が適切です。単純なタイプのみを含み、他のヘッダーファイルは含めないでください(それらが同じくらい単純な場合を除きます)。複雑なものはすべて、循環依存の問題につながります。

経験則

ヘッダーファイルには、絶対に必要なヘッダーファイルのみを含める必要があります。それ以外の場合は、ソースファイルから含める必要があります。親ファイルとして使用されるクラスが定義されている場合、またはそのクラスのメンバーオブジェクトが存在する場合、またはそのクラスのパラメーターオブジェクトを使用する場合にのみ、ヘッダーファイルが必要になります。

objectという用語は、参照やポインタと区別するために使用しています。これらを使用している場合、ヘッダーファイルを含める必要はありません。あなただけのフォワード宣言を行う必要があります。

9
Martin York

クラスCEntityを定義した時点で、クラスCPlayerCharの完全なdefinitionが表示されることを確認する必要があります。 (したがって、包含を確認してください。)

これは、完全に定義されたクラスからのみ継承でき、前方宣言されたクラスからのみ継承できないためです。

完全な定義の代わりに前方宣言を回避できるのは、型へのポインターまたは参照を作成するときだけですが、そのメンバーのいずれにもアクセスしない場合、または(@Alfのおかげで)関数を宣言するときにのみ不完全な戻り型。

4
Kerrek SB