0

I'm using two classes that depend on each other. However, when I compile the program I get a nonsensical exception description. I have reduced my code to show the error only when I include the World.h header file in the Creature header file. The exception is thrown before an opportunity to implement a forward declaration, or template. Also, preprocessor directives are not working in my case.

Creature header:

#ifndef __CREATURE_H
#define __CREATURE_H
#include "World.h"
    class Creature
    {
    public:
        //World *world; -- This class only needs a pointer to world. 
    };
#endif

World header:

#ifndef WORLD_H
#define WORLD_H
#include "Creature.h"
    class World
    {
    public:
        Creature** world;
    };
#endif

A Driver to complete the example:

#include "World.h"
int main()
{
    World world;
    return 0;
}

Visual Studio 2012's Exception message:

world.h(14): error C2143: syntax error : missing ';' before '*'
world.h(14): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

I noticed in my minimal example that Intellisense will underline an inclusion and on hover show: "Include file .. includes itself". This doesn't happen in my larger project. However, commenting the include, uncommenting the instantiation of the other class, then compiling the project produces the same error.

  • If `Creature` only needs a pointer to `World`, you can use a forward declaration instead of #including world.h. – dlf Nov 20 '14 at 01:59
  • If you only need a pointer, than why are you including the definition? Use a forward-declaration instead: `class World;`. That's a good idea even if you have no recursive dependencies you need to resolve. – Deduplicator Nov 20 '14 at 02:00

4 Answers4

2

The problem is that the compiler "sees" class Creature, complete with its World*, before it sees World. This becomes obvious if we do the #including by hand:

#ifndef WORLD_H
#define WORLD_H

   //#include "Creature.h"
      #ifndef __CREATURE_H
      #define __CREATURE_H
      #include "World.h" // the #ifndef prevents this from expanding into anything interesting
      class Creature
      {
      public:
         World *world; // here's World! Except...it hasn't been declared or defined yet. 
      };
      #endif

   // finally we get to class World, but it's too late     
   class World
   {
   public:
      Creature** world;
   };
#endif

A simple solution is to forward-declare World instead of using reciprocal includes. e.g.:

#ifndef __CREATURE_H
#define __CREATURE_H
    class World; // instead of #include "World.h"

    class Creature
    {
    public:
        World *world;
    };
#endif

And based on the code you've shown, you can do the same thing with Creature in world.h (though you only need one of the two to solve the compile problem).

dlf
  • 9,045
  • 4
  • 32
  • 58
  • Perfect. You're a few minutes too late, or I would have deemed this the best answer. –  Nov 20 '14 at 02:20
0

Use a forward declaration.

In Creature.h change

#include "World.h"

to

class World;
The Dark
  • 8,453
  • 1
  • 16
  • 19
  • Why? How does that fix it? What if there were no recursive inclusion anyway? – Deduplicator Nov 20 '14 at 02:04
  • Yes. I can compile. But I'm having difficulty accessing methods of World in Creature, using the pointer? This condition: `if(world.getAt(row,element - 1).type == ENEMY)` produces "Expression must have class type." –  Nov 20 '14 at 02:05
  • Also, this: `if(world->getAt(row, element + 1).type == ENEMY)` produces: "Pointer to incomplete class type is not allowed" ... Is this normal in this situation? –  Nov 20 '14 at 02:07
  • You can still include World.h inside Creature.cpp, that will allow your Creature methods to use the World class. You just can't put code that need to know what World is in Creature.h – The Dark Nov 20 '14 at 02:07
  • @Deduplicator - I am not sure what you mean. If there were no recursive inclusions, then there wouldn't be a problem. Or were you asking me to expand my answer? – The Dark Nov 20 '14 at 02:09
  • Yep, the first two really should be addressed in your answer to make it acceptable, the last would be a good thing to address as well. – Deduplicator Nov 20 '14 at 02:10
  • Fair enough. I think dll's answer covers it. – The Dark Nov 20 '14 at 02:13
0

I think it's a declare problem. and it's not a good idea to have "#include" in your header file.

better way: (notice i use "#pragma once" instead of "#ifndef #define")

Creature.h

#pragma once
class World;
class Creature {.........}

World.h

#pragma once
class Creature;
class World {..........}

your any .cpp file

#include "World.h"
#include "Creature.h"
Yang Yuan
  • 176
  • 1
  • 4
  • This will cause class redeclaration errors. – Giffyguy Nov 20 '14 at 02:01
  • It's perfectly fine, and even *the right thing* to have includes in a header. It just should be the minimum needed, and forward-declarations are preferable. – Deduplicator Nov 20 '14 at 02:02
  • @Giffyguy: "class redeclaration errors"? What's that? – Deduplicator Nov 20 '14 at 02:02
  • @Deduplicator If you define class Foo with all its members, and then afterwards write `class Foo;` again (declaring the class again), the compiler will/should throw an error. Recursive headers with forward declarations can cause this. – Giffyguy Nov 20 '14 at 02:04
  • @Giffyguy: Why should that be an error, instead of just useless repetition, as it is now? – Deduplicator Nov 20 '14 at 02:12
0

I recommend sending your preprocessor output to a file, and checking what the actual preprocessed code looks like.

To solve issues like this, I would create another header file with forward declarations (such as declarations.h), then include the forward declarations in each header where you define a class implementation.

The normal methods of keeping headers from being included more than once (#ifndef, #pragma once, etc.) will work flawlessly to prevent anything from being declared or defined more than once, while allowing both World and Creature to reference each other with pointers.

The key here is, the cpp file where the main function is defined, will need to include BOTH World.h and Creature.h

Giffyguy
  • 20,378
  • 34
  • 97
  • 168