Base Class Undefined

Started by
8 comments, last by GameCreator 9 years, 7 months ago

I made a few changes to my project and it now has two "bass class undefined" errors (for the same class, in the attached picture). I believe, from researching this online, that the problem is that I have circular references. However, I don't know how to remove it. To start, I would need to know what order my project is compiled in. Is there a file list that Visual Studio provides? I'm pretty sure it all starts at App.cpp, then App.h but then what's the next file? How does VS choose which CPP file comes next? Is it alphabetical? If there are 3 header files in a file, will it immediately do all of those headers next? I only have one header in each CPP file (the H file by the same name) but the headers may have 2 or 3.

Here's a screenshot of my project with the problematic class showing.

[attachment=23507:screen.gif]

Thank you for any help!

(By the way, I read that in other situations forward declaration is the way to go but that it doesn't work with base classes.)

Advertisement

How does VS choose which CPP file comes next? Is it alphabetical?

When you build your project there will usually be a window with the compiler output in the bottom. If you want to, you can read the sequence things happen out of there. However, that is not going to be relevant for your problem.

It would really help to post the relevant code (for example where is characterclass actually defined) instead of screenshots as well as complete error messages.

If there are 3 header files in a file, will it immediately do all of those headers next? I only have one header in each CPP file (the H file by the same name) but the headers may have 2 or 3.

Headers are irrelevant. The compiler only deals with .cpp files and an #include directive is just a convenient automatic way to paste the complete content of whatever file is referenced there. That happens during preprocessing before the compiler even sees it.

The most likely issues are that you did not include the header which declares characterclass or your inclusion guard did not use a unique enough define. Note that using '#pragma once' instead of manual inclusion guards would solve this problem immediately and is available on all three compilers an average person is likely to encounter (MSVC, gcc and clang).

On a more serious note, avoid 'using namespace' directives in headers. If you really need to, do that in implementation files or at function scope.


To start, I would need to know what order my project is compiled in.

Nope, the order of compilation is irrelevant.

Remember, C++ source files are compiled in isolation. For each source file, any header files that are #included are copied into one big "translation unit" by the preprocessor, and then the compiler compiles this. So what is happening is that the result of your #including either does not include the base class or it appears after the derived class.

If you actually have a circular reference, the technical solution is to use forward declarations in the header files and only to #include the relevant headers in the source file. This is good practise in general for reducing compilation time and needless recompilation.

The design solution is to remove one of the dependencies, or de-couple it by introducing an intermediary. A non-circular example of this is a class that handles input. In a simple game, this class might directly invoke functions on a Player object, for instance. To break this direct dependency, the Input class could instead bind std::function<>s to different controls, and then some higher level glue code creates the functions that call the Player functions.

Like BitMaster, I'd recommend you show us the header files that are affected.

Changing all the headers to #pragma once did not change the result.

And as I mentioned in my original post, from what I'm reading online, forward declarations don't work for base classes.

"Derived classes definitely need to know the structure of their parent, not just that the parent exists, so a forward declaration would be insufficient." from here.

But here's my entire set of code files, should you want to take a look: [attachment=23508:Source.rar]

Oh, and excuse all the globals. ;)

If there are 3 header files in a file, will it immediately do all of those headers next? I only have one header in each CPP file (the H file by the same name) but the headers may have 2 or 3.


Headers aren't compiled. They're effectively just text that gets copied into the file that includes them. e.g., this code:

// base.h
class base {};
// derived.h
#include "#base.h"
class derived : base {};
is expanded into this equivalent code:
// derived.h
// base.h
class base {};
class derived : base {};
That's all that headers do and are for. They let you write up common bits of code and paste them automatically into other bits of code.

So far as your problem, it's usually caused by a circular dependency in includes. As you can imagine from the above example, you cannot have base.h include derived.h and derived.h include base.h, as then you'd have an infinite recursion of includes. Trying to do this will cause includes to not work properly as the compiler has rules for how to avoid an infinite include chain like that. That appears to be what you're running into.

I'm not going to download a random .rar file from the Internet to pour through your code. Upload it to a code hosting site like Github or at least use something like pastebin, or better yet just include the relevant snippets here.

Sean Middleditch – Game Systems Engineer – Join my team!

A source file that includes character.h alone, such as character.cpp, will end up in a translation units that is built like this:

  1. The file character.cpp, which is being compiled, includes character.h to the translation unit.
  2. The file character.h includes world.h.
  3. The file world.h includes character.h and player.h.
  4. The file character.h is blocked by the include guard to prevent it from being included multiple times, but the file player.h is included.
  5. The file player.h includes character.h, which is blocked again by the include guards.
  6. The stack of includes falls back to character.h from step 2, and then to character.cpp from step 1.
  7. ... and so on.

Pay attention to the order in which the files are included in the final translation unit. The class player ends up in step 4, but the character class it derives from is not included in the translation unit until step 5. Thus, the base class is actually defined after the derived class despite your attempt to include the base class before defining the derived class.

The solution is very simple in this case. What is the purpose of including character.h and player.h in world.h? There's nothing in world.h that depends on the contents of neither of the two files, and those unnecessary and seemingly harmless includes cause circular includes and symbols to not be defined in the correct places within a translation unit.

Moving to For Beginners. I hope nobody rage-quits over this!

-- Tom Sloper -- sloperama.com

The solution is very simple in this case. What is the purpose of including character.h and player.h in world.h? There's nothing in world.h that depends on the contents of neither of the two files, and those unnecessary and seemingly harmless includes cause circular includes and symbols to not be defined in the correct places within a translation unit.

Thank you. world.cpp uses the player class. And I was under the impression from somewhere that you should only put one header include in cpp files whenever possible. So I put player.h in world.h instead. But when issues started happening, I added character.h as well to make sure it's included first as I thought: what's the harm? Better to make sure it's included as it can't be included twice anyway, right? And yet there are these circular references which, in my less-than-knowledgable mind, shouldn't even be possible (but I'm starting to understand a bit that the system doesn't work the way I thought it did, thanks to people's help here). I'll see what happens if I put player.h into world.cpp instead but it seems like that would do the same thing...

Thank you. world.cpp uses the player class. And I was under the impression from somewhere that you should only put one header include in cpp files whenever possible.

That is completely untrue. The aim in general is to limit the number of includes in headers files (don't include what you do not need, use forward declarations where possible). Implementation files will often need the complete definition of things they use and then you need to include all relevant headers there.

And yet there are these circular references which, in my less-than-knowledgable mind, shouldn't even be possible (but I'm starting to understand a bit that the system doesn't work the way I thought it did, thanks to people's help here).

Both the manual #ifdef/#define inclusion guards and #pragma once are not magic. They avoid repeated declaration errors but you still cannot have two headers depending on declarations in the other.

Thank you everyone. I'm going to reread the posts here to get a better grasp (lot of the terminology is new to me) and rework my includes where possible.

This topic is closed to new replies.

Advertisement