web-dev-qa-db-fra.com

Déclaration directe de classe C ++

Quand j'essaye de compiler ce code j'obtiens:

52 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h invalid use of undefined type `struct tile_tree_Apple' 
46 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h forward declaration of `struct tile_tree_Apple' 

une partie de mon code:

class tile_tree_Apple;

class tile_tree : public tile
{
      public:
          tile onDestroy() {return *new tile_grass;};
          tile tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
          void onCreate() {health=Rand()%5+4; type=TILET_TREE;};        
};

class tile_tree_Apple : public tile
{
      public:
          tile onDestroy() {return *new tile_grass;};
          tile tick() {if (Rand()%20==0) return *new tile_tree;};
          void onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;}; 
          tile onUse() {return *new tile_tree;};       
};

Je ne sais pas vraiment quoi faire, j'ai cherché la solution mais je n'ai rien trouvé de similaire à mon problème ... En fait, j'ai plus de classes avec la "tuile" parent et c'était correct avant ...

ÉDITER:

J'ai décidé de changer tous les types retournés en pointeurs pour éviter les fuites de mémoire, mais maintenant j'ai:

27 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h ISO C++ forbids declaration of `tile' with no type 
27 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h expected `;' before "tick"

Ce n'est que dans la classe de base, tout le reste est ok ... Chaque fonction de la classe de tuiles qui retourne * tuile a cette erreur ...

Du code:

class tile
{
      public:
          double health;
          tile_type type;
          *tile takeDamage(int ammount) {return this;};
          *tile onDestroy() {return this;};
          *tile onUse() {return this;};
          *tile tick() {return this};
          virtual void onCreate() {};
};
12
noisy cat

Pour new T pour compiler, T doit être un type complet. Dans votre cas, lorsque vous dites new tile_tree_Apple dans la définition de tile_tree::tick, tile_tree_Apple est incomplet (il a été déclaré en avant, mais sa définition se trouve plus loin dans votre fichier). Essayez de déplacer les définitions en ligne de vos fonctions vers un fichier source distinct, ou au moins de les déplacer après les définitions de classe.

Quelque chose comme:

class A
{
    void f1();
    void f2();
};
class B
{
   void f3();
   void f4();
};

inline void A::f1() {...}
inline void A::f2() {...}
inline void B::f3() {...}
inline void B::f4() {...}

Lorsque vous écrivez votre code de cette façon, toutes les références à A et B dans ces méthodes sont garanties de faire référence à des types complets, car il n'y a plus de références directes!

15
Armen Tsirunyan

Utilisez la déclaration directe lorsque cela est possible.

Supposons que vous souhaitiez définir une nouvelle classe B qui utilise des objets de classe A.

  1. B utilise uniquement des références ou des pointeurs vers A. Utilisez la déclaration directe, vous n'avez donc pas besoin d'inclure <A.h>. Cela accélérera à son tour un peu la compilation.

    class A ;
    
    class B 
    {
      private:
        A* fPtrA ;
      public:
        void mymethod(const& A) const ;
    } ;
    
  2. B dérive de A ou B utilise explicitement (ou implicitement) des objets de classe A. Vous devez ensuite inclure <A.h>

    #include <A.h>
    
    class B : public A 
    {
    };
    
    class C 
    {
      private:
        A fA ;
      public:
        void mymethod(A par) ;   
    }
    
17
kumaran

La déclaration directe est un "type incomplet", la seule chose que vous pouvez faire avec un tel type est d'instancier un pointeur vers lui, ou de le référencer dans une fonction declaration (ie et argument ou type de retour dans un prototype de fonction). À la ligne 52 de votre code, vous essayez d'instancier un objet.

À ce stade, le compilateur n'a aucune connaissance de la taille de l'objet ni de son constructeur, il ne peut donc pas instancier un objet.

8
Clifford

J'avais ceci:

class paulzSprite;
...

struct spriteFrame
{
    spriteFrame(int, int, paulzSprite*, int, int);
    paulzSprite* pSprite; //points to the Sprite class this struct frames
    static paulzSprite* pErase; //pointer to blanking Sprite
    int x, y;
    int Xmin, Xmax, Ymin, Ymax; //limits, leave these to individual child classes, according to bitmap size
    bool move(int, int);
    bool DrawAt(int, int);
    bool dead;
};

spriteFrame::spriteFrame(int initx, int inity, paulzSprite* pSpr, int winWidth, int winHeight)
{
    x = initx;
    y= inity;
    pSprite = pSpr;
    Xmin = Ymin = 0;
    Xmax = winWidth - pSpr->width;
    Ymax = winHeight - pSpr->height;
    dead = false;
}

...

Vous avez le même chagrin que dans la question d'origine. Résolu uniquement en déplaçant la définition de paulzSprite vers après celle de spriteFrame. Le compilateur ne devrait-il pas être plus intelligent que cela (VC++, VS 11 Beta)?

Et btw, je suis entièrement d'accord avec la remarque de Clifford ci-dessus "Les pointeurs ne provoquent pas de fuites de mémoire, un mauvais codage provoque des fuites de mémoire". À mon humble avis, cela est vrai de nombreuses autres nouvelles fonctionnalités de "codage intelligent", qui ne devraient pas se substituer à la compréhension de ce que vous demandez réellement à l'ordinateur de faire.

6
Paul Pignon

Le problème est que tick() a besoin de connaître la définition de tile_tree_Apple, mais tout ce qu'il a est une déclaration directe de celui-ci. Vous devez séparer les déclarations et définitions comme suit:

tile_tree.h

#ifndef TILE_TREE_H
#define TILE_TREE_H
#include "tile.h"

class tile_tree : public tile
{
public:
    tile onDestroy();
    tile tick();
    void onCreate();
};

#endif

tile_tree.cpp:

tile tile_tree::onDestroy() {
    return *new tile_grass;
}

tile tile_tree::tick() {
     if (Rand() % 20 == 0)
         return *new tile_tree_Apple;
}

void tile_tree::onCreate() {
    health = Rand() % 5 + 4;
    type = TILET_TREE;
}

Sauf vous avez un problème majeur: vous allouez de la mémoire (avec new), puis copiez l'objet alloué et renvoyez la copie. Cela s'appelle une fuite de mémoire , car il n'y a aucun moyen pour votre programme de libérer la mémoire qu'il utilise. Non seulement cela, mais vous copiez un tile_tree dans un tile, qui supprime les informations qui font un tile_tree différent d'un tile; c'est ce qu'on appelle découpage .

Ce que vous voulez, c'est renvoyer un pointeur vers un nouveau tile, et assurez-vous d'appeler delete à un moment donné pour libérer la mémoire:

tile* tile_tree::tick() {
     if (Rand() % 20 == 0)
         return new tile_tree_Apple;
}

Encore mieux serait de renvoyer un pointeur intelligent qui gérera la gestion de la mémoire pour vous:

#include <memory>

std::shared_ptr<tile> tile_tree::tick() {
     if (Rand() % 20 == 0)
         return std::make_shared<tile_tree_Apple>();
}
4
Jon Purdy

la classe tile_tree_Apple doit être définie dans un fichier .h distinct.

tta.h:
#include "tile.h"

class tile_tree_Apple : public tile
{
      public:
          tile onDestroy() {return *new tile_grass;};
          tile tick() {if (Rand()%20==0) return *new tile_tree;};
          void onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;}; 
          tile onUse() {return *new tile_tree;};       
};

file tt.h
#include "tile.h"

class tile_tree : public tile
{
      public:
          tile onDestroy() {return *new tile_grass;};
          tile tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
          void onCreate() {health=Rand()%5+4; type=TILET_TREE;};        
};

une autre chose: retourner une tuile et non une référence de tuile n'est pas une bonne idée, à moins qu'une tuile soit de type primitif ou très "petit".

3
vulkanino

Pour faire autre chose que déclarer un pointeur sur un objet, vous avez besoin de la définition complète.

La meilleure solution est de déplacer l'implémentation dans un fichier séparé.

Si vous devez garder cela dans un en-tête, déplacez la définition après les deux déclarations:

class tile_tree_Apple;

class tile_tree : public tile
{
  public:
      tile onDestroy();
      tile tick();
      void onCreate();        
};

class tile_tree_Apple : public tile
{
  public:
      tile onDestroy();
      tile tick();
      void onCreate(); 
      tile onUse();       
};

tile tile_tree::onDestroy() {return *new tile_grass;};
tile tile_tree::tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
void tile_tree::onCreate() {health=Rand()%5+4; type=TILET_TREE;};        

tile tile_tree_Apple::onDestroy() {return *new tile_grass;};
tile tile_tree_Apple::tick() {if (Rand()%20==0) return *new tile_tree;};
void tile_tree_Apple::onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;}; 
tile tile_tree_Apple::onUse() {return *new tile_tree;};       

Important

Vous avez des fuites de mémoire:

tile tile_tree::onDestroy() {return *new tile_grass;};

créera un objet sur le tas, que vous ne pourrez pas détruire par la suite, à moins que vous ne fassiez un vilain piratage. De plus, votre objet sera tranché. Ne faites pas cela, renvoyez un pointeur.

0
Luchian Grigore

Pour effectuer *new tile_tree_Apple le constructeur de tile_tree_Apple devrait être appelé, mais à cet endroit, le compilateur ne sait rien de tile_tree_Apple, il ne peut donc pas utiliser le constructeur.

Si vous mettez

tile tile_tree::tick() {if (Rand()%20==0) return *new tile_tree_Apple;};

dans un fichier cpp séparé qui a la définition de la classe tile_tree_Apple ou inclut le fichier d'en-tête qui a la définition, tout fonctionnera bien.

0
Seagull