Defning a namespace class in a header file

Started by
4 comments, last by jpetrie 8 years, 10 months ago

I have a similar problem to this but not the exact.

Assuming we have 2 header files and a main.cpp. In the first header file we have :


namespace Logic
{
class GameManager;
}

In the second header:


#include "first_header.h"
class Logic::GameManager 
{
public : 
void init();
void run():
};

And in the main.cpp i have :


#include "first_header.h"
int main()
{
Logic::GameManager gm;
gm.init();
gm.run();
}

I get this error until i include the second header in main.cpp :

'gm' uses undefined class 'Logic::GameManager'

And when i write the first header like this i'll get an error that init() is not a member of GameManager:


namespace Logic
{
class GameManager{};
}

-Is this way of using namespaces and classes correct ?

-Is there a better way to do this?

Thanks.

Advertisement

The reason you're getting the error is because in main(), you don't have the full definition of GameManager, you only know it exists by knowing its symbol name.

In order to instantiate anything, you have to know it's exact size, which you accomplish by including the header that has its whole definition, which is your second header.

This has nothing to do with namespaces. Namespaces are used to contain symbols in a named scope, so they don't clash with symbols with the same name from other libraries.

You should probably use namespaces like this:


//GameManagerFwd.h, used when you just need a forward declaration, but rarely actually used
namespace Logic
{
   class GameManager;
}

//GameManager.h, used when you need to instantiate the manager of call any of it's methods
namespace Logic
{
   class GameManager
   {
   public:
      void init();
      void run();
   }
}

//GameManager.cpp, here you can use two forms, where the first is more common
namespace Logic
{
   void GameManager::init()
   {
      ...
   }
}

void Logic::GameManager::run()
{
   ...
}

devstropo.blogspot.com - Random stuff about my gamedev hobby

Not sure if you have a reason for using 2 headers, but normally you put the class definition+method declarations in a .h, and define the class methods in a .cpp

as in

GameManager.h:


#pragma once
namespace Logic
{
    class GameManager()
    {
    public:
        void init(); //Use a constructor instead if you do not need two step initialization
        void run();
    };
}

GameManager.cpp:


#include "GameManager.h"

namespace Logic
{
    void GameManager::init()
    {
    //do stuff
    }
    void GameManager::run()
    {
    //do stuff
    }
}

main.cpp:


#include "GameManager.h"

int main()
{
//Use Logic::GameManager
}

o3o


'gm' uses undefined class 'Logic::GameManager'


The key words in that compiler error message are "uses" and "undefined." It's important to understand the information provided by declarations, and definitions. You might want to google for something like "C++ declaration vs. definition."

To better understand declarations and definitions, you need to think like a compiler.

Consider the line in your source code:


Logic::GameManager gm;


N.B., implicit in that line of code is a call to the public constructor (void) for GameManager. That's what the compiler considers a "use" of the class. See the following for reasons why it's complaining that the class is undefined.

Although it isn't really how compilers and linkers work, the process of compiling and linking can be thought of as follows:

When the compiler reaches that line, it attempts to generate object code that would look something like "call [public constructor Logic::GameManager()]" The bracketed [] phrase contains information which the linker will later use (after all modules have been compiled) to locate the named function in all the object code modules, and write the actual address for the function call at that point in the code. The compiler must provide sufficient information for the linker to resolve that address.

When just the first header is included, the compiler "knows" only that a namespace X containing a class Y should be found during the linking phase. That's insufficient information for the compiler to determine:

1. Whether a public constructor exists. E.g., the constructor GameManager() may be private. The public constructor hasn't been defined.

2. What the location (offset) is for the GameManager constructor function. As Stewya implies, the compiler has no information regarding where function calls will be located with regard to actual memory addresses in the final code, because the class structure hasn't been defined.

By including the second header:

1. The compiler can determine that a public constructor exists. I.e., the header information implies that somewhere in the object code there will be that function.

2. And the location within the code for the class where the constructor can be determined.

EDIT: Note that a pointer to an object is not considered a use of that object. That is:


Logic::GameManager *gm;


would be fine. The compiler "knows" how to reserve space for a pointer. That doesn't require knowing anything more about GameManager than it's a class.

However, the following, similar to the instantiation shown above, comprises a use of the class.


gm = new Logic::GameManager; // "uses" the public (void) constructor
// or
gm->Init(); // calls the address of a function in the instance

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I doubt it even gets to the point of worrying about a constructor. Without knowing the actual size of the class, it doesn't even know how much space the instance will need on the stack. In the same way, if it was a member variable, it couldn't determine the size of the enclosing class.

f@dzhttp://festini.device-zero.de

I've removed some of the controversial posts (and related discussion around them). Now back to your regularly-scheduled thread topic.

This topic is closed to new replies.

Advertisement