2012-02-02 40 views
8

Cuando intento compilar el código me sale:clase de C++ declaración adelantada

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' 

alguna parte de mi código:

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;};  
}; 

Realmente no sabía qué hacer, busqué la solución pero no pude encontrar nada similar a mi problema ... En realidad, tengo más clases con "azulejo" parental y estaba bien antes ... Gracias por cualquier ayuda.

EDIT:

decidí cambiar todos los tipos devueltos a los punteros para evitar pérdidas de memoria, pero ahora tengo:

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" 

Su único en la clase base, todo lo demás está bien ... Cada función de la clase de azulejos que devuelven * baldosas tiene este error ...

Algunos código:

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() {}; 
}; 

Respuesta

11

Para que new T compilar, T debe ser un tipo completo. En su caso, cuando dice new tile_tree_apple dentro de la definición de tile_tree::tick, tile_tree_apple está incompleto (se ha declarado hacia adelante, pero su definición está más adelante en su archivo). Intente mover las definiciones en línea de sus funciones a un archivo fuente separado, o al menos muévalas después de las definiciones de clase.

Algo así como:

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() {...} 

Cuando se escribe el código de esta manera, todas las referencias a A y B en estos métodos están garantizados para referirse a completar tipos, ya que no existen referencias más adelante!

+0

¿El 'inline' también tiene que ir en la definición de la clase? Nunca estoy demasiado seguro de esto ... –

+1

@KerrekSB: AFAIR, debe ir a la declaración o a la definición, pero no importa cuál –

+3

@KerrekSB: debe ir solo en la definición; ponerlo en la declaración no tiene ningún efecto en absoluto. – ildjarn

0

Para hacer algo más que declarar un puntero a un objeto, necesita la definición completa.

La mejor solución es mover la implementación en un archivo separado.

Si debe tener esto en una cabecera, mueva la definición después de que ambas declaraciones:

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;};  

Importante

Usted tiene pérdidas de memoria:

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

se crea un objeto en el montón, que no puedes destruir después rwards, a menos que hagas un feo hacking. Además, su objeto será cortado. No haga esto, devuelva un puntero.

+0

Esto no es cierto. Busca el estándar. Puede, por ejemplo, declarar (pero no definir) una función que toma T si T es un tipo incompleto. También puede declarar referencias a T. –

+0

¿Debo cambiar todo en clases por punteros? –

+2

[Este] (http://stackoverflow.com/questions/9107009/where-are-complete-types-not-required/9107130#9107130) debería ser útil, –

-2

Soy un novato CPP, pero ¿no tienes que especificar la herencia en la declaración forward?

class tile_tree_apple : public tile;

+4

No, no es así. . – Joe

+0

No, entonces el compilador necesitaría toda la declaración ... –

5

La declaración hacia adelante es un "incompleta tipo", lo único que puede hacer con tal tipo es una instancia de un punteroa ella, o hacer referencia a ella en una función declaración (es decir, y argumento o tipo de retorno en un prototipo de función). En la línea 52 de su código, está intentando crear una instancia de un objeto .

En ese momento, el compilador no tiene conocimiento del tamaño del objeto ni de su constructor, por lo que no puede crear una instancia de un objeto.

+2

Hay muchas más cosas que puede hacer con un tipo incompleto. –

+1

¿Cómo se crea una instancia de un puntero sin instanciar un objeto? –

+0

@Luchian: en este caso: 'tile_tree_apple * tta_ptr;' Instancia un puntero de tipo 'tile_tree_apple *', aunque, por supuesto, no apunta a un objeto válido. El punto es que podría tener un puntero como miembro de una clase que más tarde instancia el objeto en el constructor, por ejemplo, pero de cualquier manera, en el punto en el código donde el tipo * complete * es visible. – Clifford

3

clase tile_tree_apple debe definirse en un archivo .h separado.

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;};   
}; 

otra cosa: devolver una ficha y no una referencia del azulejo no es una buena idea, a menos que una baldosa es un tipo primitivo o muy "pequeño".

+0

¿No será necesario que tile_tree_apple sepa sobre tile_tree a través de una inclusión y viceversa? – Bren

4

El problema es que tick() necesita conocer la definición de tile_tree_apple, pero todo lo que tiene es una declaración directa de ello. Usted debe separar las declaraciones y definiciones de esta manera:

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; 
} 

Excepto que tienen un gran problema: que estés asignación de memoria (con new), luego copia el objeto asignado y devuelve la copia. Esto se denomina pérdida de memoria, porque no hay forma de que su programa libere la memoria que utiliza. No solo eso, sino que está copiando un tile_tree en un tile, que descarta la información que hace que un tile_tree sea diferente de un tile; esto se llama rebanando.

Lo que queremos es devolver un puntero a una nueva tile, y asegúrese de llamar delete en algún momento para liberar la memoria:

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

Aún mejor sería devolver un puntero inteligente que se encargará la gestión de memoria para usted:

#include <memory> 

std::shared_ptr<tile> tile_tree::tick() { 
    if (rand() % 20 == 0) 
     return std::make_shared<tile_tree_apple>(); 
} 
0

para realizar *new tile_tree_apple el constructor de tile_tree_apple debe ser llamado, pero en este lugar compilador sabe nada acerca de tile_tree_apple, por lo que ça no usar el constructor

Si pones

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

en cpp separado que tiene la definición de clase o tile_tree_apple incluye el fichero de cabecera que tiene la definición que todo funciona bien.

15

Usa la declaración directa siempre que sea posible.

Supongamos que quiere definir una nueva clase B que utiliza objetos de la clase A.

  1. B sólo se utiliza referencias o punteros a A. Use la declaración de reenvío luego no necesita incluir <A.h>. Esto a su vez acelerará un poco la compilación.

    class A ; 
    
    class B 
    { 
        private: 
        A* fPtrA ; 
        public: 
        void mymethod(const& A) const ; 
    } ; 
    
  2. B deriva de A o B explícitamente (o implícitamente) utiliza objetos de la clase A. A continuación, deberá incluir <A.h>

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

Debería ser: void mymethod (const A &) const. – KIIV

+0

Esta respuesta (n. ° 1) contiene error, por lo que he votado negativamente. –

5

Tenía esta:

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; 
} 

...

consiguió el mismo dolor como en la pregunta original. Solo se resuelve moviendo la definición de paulzSprite a después de la de spriteFrame. ¿No debería el compilador ser más inteligente que esto (VC++, VS 11 Beta)?

Y, por cierto, estoy totalmente de acuerdo con la observación de Clifford anterior "Los indicadores no causan pérdidas de memoria, la codificación deficiente provoca pérdidas de memoria". En mi humilde opinión, esto es cierto de muchas otras características nuevas de "codificación inteligente", que no deberían ser un sustituto para comprender lo que realmente le pides a la computadora que haga.

+0

Hola y bienvenidos a Stack Overflow. No utilizamos firmas aquí, así que no se ofenda si/cuando se elimina su firma – Jeff