2

What's the difference between declaring static variables inside a class, in the .h file or in the class definition .cpp file?

Example:

//File.h
class File
{
private:
    const static int number = 10;    
public:
    File();
    ~File();
};

or

//File.cpp

#include "File.h"

const static int number = 10;

File::File()
{
}

File::~File()
{
}

The idea is to use those variables for every instance of File.

user3900456
  • 2,003
  • 3
  • 25
  • 33
  • The first one is a declaration. The second one is a definition. They aren't the same thing, and both are required for static member variables. – user207421 Feb 27 '20 at 05:43
  • 2
    If you wanted to have it *only* in the header, you'd could use *inline* initialisation: `inline static const int number = 10;`. Having it in the cpp file though means you don't have to recompile all the translation units that include the header file every time the value is changed - just relink. That only tends to matter in large corporate software environments, or when you distribute libraries to clients. – Tony Delroy Feb 27 '20 at 05:56

4 Answers4

3

You declare the variables inside the class definition, then you define them in a source file.

But you need to use the proper scope when defining the variable, just like you use for member functions:

const int File::number = 10;
//        ^^^^^^
// Note scope here
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • what if I don't want "users" of my class to see that variable? is that ok to move it completely to .cpp? – user3900456 Feb 27 '20 at 05:38
  • 2
    @user3900456: having removed a member from the class definition in the header, you can't add one back in in the cpp file. You can, however, have a non-member variable in the cpp file - it will just be visible to everything in the cpp - not just the `File` functions. These days it's common to put it in an anonymous namespace - indicating the linker doesn't need to make it findable for use by other objects, rather than making it `static`. So, ideally you'd have `namespace { const int number = 10; }`. – Tony Delroy Feb 27 '20 at 05:46
  • @TonyDelroy in that case, is the const keyword necessary? – user3900456 Feb 27 '20 at 05:54
  • 1
    @user3900456: use `const` if you want the variable to be "constant" - i.e. you don't ever want to assign new values to it with say `++number` or `number = 3;` or `number *= 2` or similar.... That way you ensure nobody accidentally changes the value after the definition, and it's easier to reason about what the value will be at the points of usage: other programmers maintaining the code will find that helpful, and the compiler may produce better (faster and/or smaller) code in some rare cases. – Tony Delroy Feb 27 '20 at 05:59
  • @TonyDelroy sorry! I meant static... wouldn't it be static already if it's in a .cpp file? – user3900456 Feb 27 '20 at 06:00
  • 4
    @user3900456: no - being `static` affects whether, when you compile an object using the cpp file, the `number` variable is exposed so other objects you link with can also access it. Being in the `cpp` doesn't automatically make it `static`. If no other objects need to access the value, you can make it `static` *or* (better) use an anonymous namespace (see my first comment above). – Tony Delroy Feb 27 '20 at 06:03
  • @user3900456 Unfortunately, static has too many meanings. Depending on the context it's used, it has a different effect, I'm already an experienced programmer and still get confused about from time to time. – JVApen Feb 27 '20 at 07:07
3

The answer to your question depends on the c++ standard you are using. If you are using an old standard as C++11 or C++14 (Ignoring the acciant C++98/03), you have to put the instantiation of the variable in the ccp:

//File.h
class File
{
private:
    const static int number;    
public:
    File() = default;
    ~File() = default;
};

//File.cpp   
#include "File.h"

const int File::number = 10;

This because ODR, the one definition rule. It states that every variable or function can only exist in a single compilation unit. This unless declared inline, in which case all compilation units should have the same definition. (No diagnostic required if violated)

From c++17 (and the upcoming C++20, which is being formalized), variables can, like functions before, be declared inline, this allows you to write the following:

//File.h
class File
{
private:
    inline const static int number = 10;    
public:
    File() = default;
    ~File() = default;
};

The big difference here is that when you change number, you need to recompile all compilation units that include the header, this to reduce typing.The additional advantage is that your compiler can use the information to optimize the code using it, as it can inline the variable. Also the users of your class, like yourself debugging in a few months, can easer see what's going on.

In this specific case, we are looking at an int, so, I would even go a step further and declare the variable as constexpr:

//File.h
class File
{
private:
    constexpr static int number = 10;    
public:
    File() = default;
    ~File() = default;
};

Constexpr variables are implicit const and inline, reducing some typing. They can also be used in constexpr context, as template argument ... And assuming you wrote a calculation (1+2+3+4), if forces your compiler to calculate and store the calculated value even in debug builds.

A side note, given in comments by Tony, you can as well put your code in an unnamed namespace in the cpp, also this is possible with constexpr. This makes a lot of sense if you don't expose any usage of this variable to other compilation units. (Private, no friends and no usage in inline functions)

 namespace { const int number = 10; }
 namespace { constexpr int number = 10; }
JVApen
  • 11,008
  • 5
  • 31
  • 67
1

From cpp forum reference (more).

If a header file is included in multiple cpp files then initialising (and hence defining it) a variable in it will break the one definition rule. The variable must be only declared in the header and then defined (and initialized) in a single .cpp file.

To summarize it's not a good idea to initialize a variable in header file. Few more links if you need. st1 , st2

  • With a small tweak, defining it in the header is completely possible (and I would even recommend it) – JVApen Feb 27 '20 at 07:01
  • The question is about putting the variable either as member into the class in the h file or into the cpp file. It is not about declaring a global variable in a h file. – Werner Henze Feb 27 '20 at 16:58
1

Putting number into the class declaration makes it a member of the class. So everyone who is including the header file and has the access rights can access the member.

In your second case the variable number is only visible in the cpp file where you define it, not in other cpp files.

If like in your case the number member is made private and the implementation of the class is all in one cpp file only and not scattered over multiples cpp files, then there is no difference for you in both variants. Then it is just a matter of style or preference. If the cpp file for this class is very big and you splitted it up into multiple cpp files, and at least two of them need access to number, then it would be better to have the member in the class than to have one variable in each file (each having the same local name and value, but different identity).

BTW, when putting it into the cpp file you should not write static const int number = 10; but put it into an anonymous namespace namespace { constexpr int number = 10; } (SF.22 in Core Guidelines). Please note that constexpr also has some advantages over const.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69