6

So far, I've been using classes the following way:

GameEngine.h declares the class as follows

class GameEngine {
public:
    // Declaration of constructor and public methods

private:
    InputManager inputManager;
    int a, b, c;

    // Declaration of private methods
};

My GameEngine.cpp files then just implement the methods

#include "____.h"    
GameEngine::GameEngine() {

}

void GameEngine::run() {
    // stuff
}

However, I've recently read that variable declarations are not supposed to be in the header file. In the above example, that would be an inputManager and a, b, c.

Now, I've been searching for where to put the variable declarations, the closest answer I found was this: Variable declaration in a header file

However, I'm not sure if the use of extern would make sense here; I just declare private variables that will only be used in an instance of the class itself. Are my variable declarations in the header files fine? Or should I put them elsewhere? If I should put them in the cpp file, do they go directly under the #include?

Community
  • 1
  • 1
Kefir
  • 85
  • 1
  • 1
  • 5
  • 7
    *"variable declarations are not supposed to be in the header file"*.... what? The member variables **must** be declared with the class declaration. – Cory Kramer Feb 14 '15 at 16:20
  • If the variable is meant to be used globally, then extern goes into the header, and you define the variable **once** in some .cpp file. If they're global variables used internally, then they go into the .cpp. If they're logically part of the class, then make them member variables. –  Feb 14 '15 at 16:21
  • Variable declarations and *member* variable declarations are different. And besides, it's the *definition* of non-member variables that will give you problems, not the declarations. – Some programmer dude Feb 14 '15 at 16:22
  • 2
    Did you mean to use the [Pimpl idiom](https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&cad=rja&uact=8&ved=0CC0QFjAC&url=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F60570%2Fwhy-should-the-pimpl-idiom-be-used&ei=M3bfVIHgCo_VapmegfAD&usg=AFQjCNFGS1PNdAv2TYM_Y_eh4mifRiD9Cw&sig2=D-gjYBLP3qSHvhmBxj-9zw&bvm=bv.85970519,d.d24)? I don't get what you have read about there. – πάντα ῥεῖ Feb 14 '15 at 16:22
  • Tried but couldn't find the original statement anymore. It was somewhere on SO, actually, but since I tried looking for an answer, my history is too full to locate it again. To me it sounded like a general statement that declaring variables in .h files was bad programming style; However, considering your reactions, I probably misunderstood it. – Kefir Feb 14 '15 at 16:39

3 Answers3

8

Don't confuse a type's members with variables. A class/struct definition is merely describing what constitutes a type, without actually declaring the existence of any variables, anything to be constructed on memory, anything addressable.

In the traditional sense, modern class design practices recommend you pretend they are "black boxes": stuff goes in, they can perform certain tasks, maybe output some other info. We do this with class methods all the time, briefly describing their signature on the .h/.hpp/.hxx file and hiding the implementation details in the .cpp/.cc/.cxx file.

While the same philosophy can be applied to members, the current state of C++, how translation units are compiled individually make this way harder to implement. There's certainly nothing "out of the box" that helps you here. The basic, fundamental problem is that for almost anything to use your class, it kind of needs to know the size in bytes, and this is something constrained by the member fields and the order of declaration. Even if they're private and nothing outside the scope of the type should be able to manipulate them, they still need to briefly know what they are.

If you actually want to hide this information to outsiders, certain idioms such as PImpl and inlined PImpl can help. But I'd recommend you don't go this way unless you're actually:

  1. Writing a library with a semi-stable ABI, even if you make tons of changes.
  2. Need to hide non-portable, platform-specific code.
  3. Need to reduce pre-processor times due to an abundance of includes.
  4. Need to reduce compile times directly impacted by this exposure of information.

What the guideline is actually talking about is to never declare global variables in headers. Any translation unit that takes advantage of your header, even if indirectly, will end up declaring its own global variable as per header instructions. Everything will compile just fine when examined individually, but the linker will complain that you have more than one definition for the same thing (which is a big no-no in C++)

If you need to reserve memory / construct something and bind it to a variable's name, always try to make that happen in the source file(s).

ZaldronGG
  • 924
  • 8
  • 13
  • I have a followup question here: is the line Inputmanager inputManager; just a declaration, or will it try to reserve memory already? I originally wanted to initialize inputManager in the constructor of GameEngine, however, it wouldn't compile because there was no default constructor for InputManager. (There was no default constructor, but I didn't call a constructor without arguments anywhere, either.) This surprised me because i just wanted to declare the existance of the variable, but it seemed like it needed a default constructor right at the declaration. – Kefir Feb 14 '15 at 16:52
  • As it stands in your class declaration, you are declaring that **it contains an InputManager in its entirety** but **nothing was actually built so far**. As soon as you create a GameEngine object in your runtime via the constructor, it will have a section of bytes entirely dedicated to storing a whole InputManager in it. The constructor will also attempt to build an InputManager. (if you don't explicitly explain how to build one, and no default constructor exists, it stands to reason that it will complain about it during compilation) 'Ownership' in C++ is a topic too broad for a comment. – ZaldronGG Feb 14 '15 at 17:36
5

Class member variables must be declared in the class definition, which is usually in a header file. This should be done without any extern keywords, completely normally, like you have been doing so far.

Only variables that are not class members and that need to be declared in a header file should be declared extern.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
3

As a general rule:

Variables that are to be used with many functions in the same class go in the class declaration.

Temporary variables for individual functions go in the functions themselves.

It seems that InputManager inputManager; belongs in the class header.

int a, b, c; is harder to know from here. What are they used for? They look like temporary variables that would be better off in the function(s) they're used in, but I can't say for sure without proper context.

extern has no use here.

Michael Gazonda
  • 2,720
  • 1
  • 17
  • 33