objects - how to implement classes in c++
DeclaraciĆ³n adelante clase C++ (9)
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. 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 tiene un problema importante: está asignando memoria (con new
), luego copiando el objeto asignado y devolviendo 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ás copiando un tile_tree
en un tile
, que descarta la información que hace que un tile_tree
diferente de un tile
; esto se llama cortar .
Lo que desea es devolver un puntero a un nuevo tile
y asegúrese de llamar a 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 manejará la administración de la memoria por usted:
#include <memory>
std::shared_ptr<tile> tile_tree::tick() {
if (rand() % 20 == 0)
return std::make_shared<tile_tree_apple>();
}
Cuando intento compilar este código, obtengo:
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 sé qué hacer, busqué la solución, pero no pude encontrar nada parecido a mi problema ... De hecho, tengo más clases con el "azulejo" para padres y estuvo bien antes ... Gracias por cualquier ayuda.
EDITAR:
Decidí cambiar todos los tipos devueltos por punteros para evitar fugas de memoria, pero ahora obtuve:
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"
Es solo en la clase base, todo lo demás está bien ... Cada función en la clase de mosaico que devuelve * tile tiene este error ...
Cierto 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() {};
};
La declaración directa es un " tipo incompleto ", lo único que puede hacer con dicho tipo es crear una instancia de un puntero a la misma, o hacer referencia a ella en una declaración de función (es decir, 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 punto, el compilador no tiene conocimiento del tamaño del objeto ni de su constructor, por lo que no puede instanciar un objeto.
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 mantener esto en un encabezado, mueva la definición después de 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
Tienes pérdidas de memoria:
tile tile_tree::onDestroy() {return *new tile_grass;};
creará un objeto en el montón, que luego no podrás destruir, a menos que realices algún hacking feo. Además, su objeto será cortado. No hagas esto, devuelve un puntero.
Para que se compile new T
, 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 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 escribe su código de esta manera, se garantiza que todas las referencias a A y B en estos métodos se refieren a tipos completos, ¡ya que no hay más referencias hacia adelante!
Para realizar *new tile_tree_apple
debe *new tile_tree_apple
al constructor de tile_tree_apple
, pero en este lugar el compilador no sabe nada sobre tile_tree_apple
, por lo que no puede usar el constructor.
Si pones
tile tile_tree::tick() {if (rand()%20==0) return *new tile_tree_apple;};
en un archivo cpp separado que tiene la definición de clase tile_tree_apple o incluye el archivo de encabezado que tiene la definición, todo funcionará bien.
Soy un novato CPP, pero ¿no tienes que especificar la herencia en la declaración forward?
class tile_tree_apple : public tile;
Tenía esto:
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;
}
...
Tengo el mismo dolor que en la pregunta original. Solo se resolvió moviendo la definición de paulzSprite a después 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.
Use la declaración directa cuando sea posible.
Supongamos que quiere definir una nueva clase B
que use objetos de la clase A
B
solo usa referencias o punteros aA
Use la declaración de reenvío, entonces no necesita incluir<Ah>
. Esto a su vez acelerará un poco la compilación.class A ; class B { private: A* fPtrA ; public: void mymethod(const& A) const ; } ;
B
deriva deA
oB
explícitamente (o implícitamente) usa objetos de la claseA
Luego necesita incluir<Ah>
#include <A.h> class B : public A { }; class C { private: A fA ; public: void mymethod(A par) ; }
clase tile_tree_apple debe definirse en un archivo .h por 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 de ficha no es una buena idea, a menos que una ficha sea de tipo primitivo o muy "pequeño".