недопустимое использование неполного типа/упреждающего объявления

Я попытался посмотреть на похожие проблемы, перечисленные здесь, в Stackoverflow и в Google, но они в основном связаны с шаблонами, и это не мой случай. Я использую GCC 4.4.5 в Debian Testing 64bit.
Итак, у меня есть два класса — 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

и CPlayerChar, который наследуется от CEntity:

#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;' в CPlayerChar.h в строке 9, но тогда вместо этого будет выдано это

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

Таким образом, предварительная декларация должна быть там. Кроме того, CEntity явно является классом, а не структурой.


person rivon    schedule 08.08.2011    source источник
comment
В C++ классы и структуры почти идентичны; не зацикливайтесь на сообщении об ошибке.   -  person Mark Ransom    schedule 09.08.2011
comment
Я предполагаю, что определение класса для CEntity находится в файле CEntity.h? Если нет, то это ваши проблемы.   -  person Mark Ransom    schedule 09.08.2011
comment
У вас есть циклическое включение в ваши заголовочные файлы. Заголовочный файл должен включать другие заголовочные файлы только в том случае, если это явно необходимо. Если вы можете использовать предварительное объявление, то это должно быть предпочтительным.   -  person Martin York    schedule 09.08.2011


Ответы (3)


У вас циклическое включение в заголовочные файлы.
Но без всех заголовочных файлов мы не сможем это исправить.

Я бы начал здесь.

#include "CAnimation.h"

Глядя на ваш заголовок, вам это на самом деле не нужно. Вы используете CAnimation только по ссылке или указателю, поэтому предварительного объявления, которое у вас есть, должно быть достаточно. Переместите включение в исходный файл (т.е. из заголовка).

Следующее место, которое я бы посмотрел, это:

#include "global_includes.h"

Любые глобальные включения, включенные в заголовочный файл, должны быть очень простыми. Должен содержать только простые типы и не включать никаких других заголовочных файлов (если они не такие же простые). Все сложное приведет к проблемам с циклическими зависимостями.

Общее правило

Заголовочный файл должен включать только те заголовочные файлы, которые ему абсолютно необходимы. В противном случае они должны быть включены из исходного файла. Вам абсолютно необходим заголовочный файл только в том случае, если он определяет класс, который используется в качестве родительского класса, у вас есть объекты-члены этого класса или вы используете объекты параметров этого класса.

Я использую термин object, чтобы отличать его от ссылок или указателей. Если вы используете их, вам не нужно включать заголовочный файл. Вам нужно только сделать предварительную декларацию.

person Martin York    schedule 08.08.2011
comment
Что касается циклических зависимостей, если у него есть #ifndef CANIMATION_H_INCLUDED, не следует ли предотвратить проблему? - person antogerva; 06.03.2017
comment
@antogerva Это помогает, но этого недостаточно. Вам также необходимо перенаправить типы объявления. Также включайте только те заголовки, которые вам нужны, и вперед объявляйте другие типы. - person Martin York; 06.03.2017

У вас, вероятно, есть цикл в ваших включениях таким образом, что CPlayerChar не знает, кто на самом деле CEntity, он просто знает, что он существует, но не знает, что это такое.

Если вы удалите объявление «class CEntity», вы увидите, что GCC будет жаловаться, что CEntity не существует.

Вы должны убедиться, что ничего из того, что включает CEntity, не включает CPlayerChar.

person André Puel    schedule 08.08.2011
comment
Спасибо, это твоя последняя строчка. CEntity включает еще один файл, в который входят CEntity и CPlayerChar. Теперь мне просто нужно найти обходной путь. - person rivon; 09.08.2011

Вы должны убедиться, что полное определение класса CEntity видно в точке, где вы определяете класс CPlayerChar. (Поэтому проверьте свои включения.)

Это связано с тем, что вы можете наследовать только от полностью определенных классов, но не только от предварительно объявленных.

Единственный раз, когда вы можете обойтись без предварительных объявлений вместо полных определений, это когда вы создаете указатели или ссылки на тип, но только если вы никогда не обращаетесь ни к одному из его членов или (спасибо @Alf) при объявлении функции с неполный тип возврата.

person Kerrek SB    schedule 08.08.2011
comment
предварительные объявления немного мощнее, чем вы, кажется, указываете. Например, если T — неполный тип, то объявление функции T foo() вполне допустимо. Чтобы вызвать его, вам нужно иметь завершенный T (если только T не является специальным типом void, который формально является неполным типом, который никогда не может быть завершен), но это другой вопрос. :-) - person Cheers and hth. - Alf; 09.08.2011
comment
@Альф: Спасибо! Это полезно. А как насчет параметров функции, они могут быть неполными? - person Kerrek SB; 09.08.2011