C++ class forward declaration
Solution 1
In order for new T
to compile, T
must be a complete type. In your case, when you say new tile_tree_apple
inside the definition of tile_tree::tick
, tile_tree_apple
is incomplete (it has been forward declared, but its definition is later in your file). Try moving the inline definitions of your functions to a separate source file, or at least move them after the class definitions.
Something like:
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() {...}
When you write your code this way, all references to A and B in these methods are guaranteed to refer to complete types, since there are no more forward references!
Solution 2
Use forward declaration when possible.
Suppose you want to define a new class B
that uses objects of class A
.
B
only uses references or pointers toA
. Use forward declaration then you don't need to include<A.h>
. This will in turn speed a little bit the compilation.class A ; class B { private: A* fPtrA ; public: void mymethod(const& A) const ; } ;
B
derives fromA
orB
explicitely (or implicitely) uses objects of classA
. You then need to include<A.h>
#include <A.h> class B : public A { }; class C { private: A fA ; public: void mymethod(A par) ; }
Solution 3
The forward declaration is an "incomplete type", the only thing you can do with such a type is instantiate a pointer to it, or reference it in a function declaration (i.e. and argument or return type in a function prototype). In line 52 in your code, you are attempting to instantiate an object.
At that point the compiler has no knowledge of the object's size nor its constructor, so cannot instantiate an object.
Solution 4
I had this:
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;
}
...
Got the same grief as in the original question. Only solved by moving the definition of paulzSprite to after that of spriteFrame. Shouldn't the compiler be smarter than this (VC++, VS 11 Beta)?
And btw, I wholeheartedly agree with Clifford's remark above "Pointers don't cause memory leaks, poor coding causes memory leaks". IMHO this is true of many other new "smart coding" features, which should not become a substitute for understanding what you are actually asking the computer to do.
Solution 5
The problem is that tick()
needs to know the definition of tile_tree_apple
, but all it has is a forward declaration of it. You should separate the declarations and definitions like so:
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;
}
Except you have a major problem: you’re allocating memory (with new
), then copying the allocated object and returning the copy. This is called a memory leak, because there’s no way for your program to free the memory it uses. Not only that, but you’re copying a tile_tree
into a tile
, which discards the information that makes a tile_tree
different from a tile
; this is called slicing.
What you want is to return a pointer to a new tile
, and make sure you call delete
at some point to free the memory:
tile* tile_tree::tick() {
if (rand() % 20 == 0)
return new tile_tree_apple;
}
Even better would be to return a smart pointer that will handle the memory management for you:
#include <memory>
std::shared_ptr<tile> tile_tree::tick() {
if (rand() % 20 == 0)
return std::make_shared<tile_tree_apple>();
}
Related videos on Youtube
noisy cat
C, C++, C#, Java. Working as Unreal developer at Layopi Games.
Updated on December 15, 2020Comments
-
noisy cat over 3 years
When I try to compile this code, I get:
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'
some part of my 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;}; };
I dont really know what to do, I searched for the solution but I couldn't find anything simmilar to my problem... Actually, I have more classes with parent "tile" and It was ok before...
EDIT:
I decided to change all returned types to pointers to avoid memory leaks, but now I got:
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"
Its only in base class, everything else is ok... Every function in tile class which return *tile has this error...
Some 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() {}; };
-
Armen Tsirunyan over 12 yearsThis isn't true. Look up the standard. You can, for example, declare (but not define) a function that takes T if T is an incomplete type. You can also declare references to T.
-
Alok Save over 12 yearsThis should be useful,
-
Kerrek SB over 12 yearsThe return type of a function may also be incomplete.
-
Kerrek SB over 12 yearsThere are many more things you can do with an incomplete type.
-
Kerrek SB over 12 yearsDoesn't the
inline
also have to go in the class definition? I'm never too sure about this... -
Armen Tsirunyan over 12 years@KerrekSB: AFAIR, it should go either to the declaration or the definition, but doesn't matter which one
-
ildjarn over 12 years@KerrekSB : It needs to go on the definition only; putting it on the declaration has no effect whatsoever.
-
Luchian Grigore over 12 yearsHow does one instantiate a pointer without instantiating an object?
-
noisy cat over 12 yearsWORKED! Give me 5 minutes and I accept it :D (due to site limits) And I have to change everything to pointers, hate memory leaks :D... Java was doing done everything with pointers for me xD
-
Armen Tsirunyan over 12 years@kittyPL: Use smart pointers to avoid memory leaks
-
noisy cat over 12 yearsI'd be really pleased If you can also help me with question in EDIT :)
-
Armen Tsirunyan over 12 years@kittyPL: replace
*tile
withtile*
and do not ignore my comment about smart pointers. They are really useful. -
Clifford over 12 years@Luchian: In this case:
tile_tree_apple* tta_ptr ;
Instantiates a pointer of typetile_tree_apple*
, although of course it does not point to a valid object. The point is you might have such a pointer as a member of a class that later instantiates the object in the constructor for example, but either way, at point in the code where the complete type is visible. -
Clifford over 12 years@kittyPL: Pointers don't cause memory leaks, poor coding causes memory leaks.
-
Clifford over 12 years@Kerrek: Maybe, but perhaps none that are relevant to this discussion. It would be useful though perhaps if you could elaborate. That said looking at your comment time, I may have already covered them in the last edit.
-
Bren about 8 yearsWon't tile_tree_apple need to know about tile_tree though an include and vice-versa?
-
KIIV almost 8 yearsIt should be: void mymethod(const A &) const.