C++ class forward declaration

166,352

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.

  1. B only uses references or pointers to A. 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 ;
    } ;
    
  2. B derives from A or B explicitely (or implicitely) uses objects of class A. 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>();
}
Share:
166,352

Related videos on Youtube

noisy cat
Author by

noisy cat

C, C++, C#, Java. Working as Unreal developer at Layopi Games.

Updated on December 15, 2020

Comments

  • noisy cat
    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
    Armen Tsirunyan over 12 years
    This 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
    Alok Save over 12 years
    This should be useful,
  • Kerrek SB
    Kerrek SB over 12 years
    The return type of a function may also be incomplete.
  • Kerrek SB
    Kerrek SB over 12 years
    There are many more things you can do with an incomplete type.
  • Kerrek SB
    Kerrek SB over 12 years
    Doesn't the inline also have to go in the class definition? I'm never too sure about this...
  • Armen Tsirunyan
    Armen Tsirunyan over 12 years
    @KerrekSB: AFAIR, it should go either to the declaration or the definition, but doesn't matter which one
  • ildjarn
    ildjarn over 12 years
    @KerrekSB : It needs to go on the definition only; putting it on the declaration has no effect whatsoever.
  • Luchian Grigore
    Luchian Grigore over 12 years
    How does one instantiate a pointer without instantiating an object?
  • noisy cat
    noisy cat over 12 years
    WORKED! 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
    Armen Tsirunyan over 12 years
    @kittyPL: Use smart pointers to avoid memory leaks
  • noisy cat
    noisy cat over 12 years
    I'd be really pleased If you can also help me with question in EDIT :)
  • Armen Tsirunyan
    Armen Tsirunyan over 12 years
    @kittyPL: replace *tile with tile* and do not ignore my comment about smart pointers. They are really useful.
  • Clifford
    Clifford over 12 years
    @Luchian: In this case: tile_tree_apple* tta_ptr ; Instantiates a pointer of type tile_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
    Clifford over 12 years
    @kittyPL: Pointers don't cause memory leaks, poor coding causes memory leaks.
  • Clifford
    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
    Bren about 8 years
    Won't tile_tree_apple need to know about tile_tree though an include and vice-versa?
  • KIIV
    KIIV almost 8 years
    It should be: void mymethod(const A &) const.