Question about static variables

Pages: 12
Hi!

I'm currently coding a game and I'm facing an issue.

I have several classes, of which many include the same header file Settings.h.

In this header, I have defined several static global variables.

For the purpose of this post let's say the settings header looks like this:
1
2
3
4
5
6
#ifndef SETTINGS_H
#define SETTINGS_H

static bool boolVariable;

#endif 


My issue is that when I modify a variable from a class, I find this variable unchanged if I call
it from another class.

My question is therefore: For this example, is an instance of boolVariable created for each
class that includes Settings.h?


Thanks in advance,

Regards,

Hugo.
Last edited on
Yes. That's what the keyword "static" is doing in this case. It makes the variable "local" to the compilation unit that it appears in. So each gets it's own copy.

Choose one cpp file to define the variables in. And in Settings.h, make them all "extern".
Thank you for that clarification.

Choose one cpp file to define the variables in. And in Settings.h, make them all "extern".

Do you mean something like this?
Settings.h:
1
2
3
4
5
6
#ifndef SETTINGS_H
#define SETTINGS_H

extern bool boolVariable;

#endif  

Settings.cpp:
1
2
3
#include "Settings.h

bool boolVariable;
Last edited on
Sure, that will do. You might want to explicitly initialize it there, too.
Basically, saying something like "extern int extVar;" in a cpp file (perhaps included from a .h file) tells the compiler that the variable exists in another compilation unit and will be resolved by the linker. So you need to actually have the variable defined in one of the cpp files.
Last edited on
Great. My last question would be the following, then.

This looks like I have to redefine the global variable in each of the cpp file I want to use it in

Assume we have this:
Settings.h:
1
2
3
4
5
6
#ifndef SETTINGS_H
#define SETTINGS_H

extern bool boolVariable;

#endif   

Settings.cpp:
1
2
3
#include "Settings.h

bool boolVariable;

Class foo:
1
2
3
4
class Foo
{
    //[...]
}

Class bar:
1
2
3
4
class Bar
{
    //[...]
}


How can I use the variable defined in settings.cpp from foo and bar cpp files,
without redefining the global variable in the class cpp files?

can't you make settings.h have an accessor function that just returns either the value ( a little safer) or a reference (typical global variable free for all) to the variable?

you can also wrap a global in a class as a static item so that simply making an instance of the class gives you access to it (directly or via accessors, your pick). This is a little safer, for whatever that is worth. Why are you playing with globals, is this just an example for something else you need to do, or ??


Just including Settings.h in the files that want to access the external variables gives those files access.
That's what the extern keyword does.
Read my last response again.
@tpb
My bad I just ran a driver program to test it, I thing I understand how it works now. Thanks a lot!

@jonnin
Well actually I though about what you said but I don't want to make them into a class because it would be a hassle to use them as arguments and it would make my code significantly longer.

I'm playing around with global variable for a game I'm working on. I have a cpp file with all the global settings for the game to run. Such as key bindings, window, renderer, screen properties etc.

And since I'm loading these variables from a binary file, I only really touch them once. Then they are merely passed as arguments for several functions. So I think that would be "safe" enough.

What makes everybody so concerned about using global variables?
jonnin is right about trying to do without the globals, although a single global object for overall settings can be quite useful. But you should at least gather them together into a single object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// main.cpp
#include <iostream>
#include "settings.h"
#include "f.h"

int main() {
    std::cout << settings.a << '\n';
    f();
    std::cout << settings.a << '\n';
}

// setting.h
#ifndef SETTINGS_H_
#define SETTINGS_H_

struct Settings {
    static int a;
    static int b;
};

extern Settings settings;

#endif

// settings.cpp
#include "settings.h"

int Settings::a = 1;
int Settings::b = 2;

// f.h
#ifndef F_H_
#define F_H_

void f();

#endif

// f.cpp
#include "settings.h"

void f() {
    settings.a = 42;
}



$ g++ -c main.cpp
$ g++ -c settings.cpp
$ g++ -c f.cpp
$ g++ main.o settings.o f.o
$ ./a.out
1
42

@tpb Thanks so much for your time, that helped a lot!
Last edited on
The main fear of global variables is that any function anywhere in the code can change it at any time, making it hard to debug. This is multiplied significantly in multi-threaded code or event driven code. They can also cause name confusion collisions with local variables etc. The bigger the code base, the more trouble they tend to cause.

That said I am not a total nay-sayer, if you have a good reason. But then again, I will do pretty much anything that works ;)

There are at least 3, maybe 4 ways to make globals safer and cleaner as well..
- you can namespace wrap them
- you can do the static class variable trick
- you can wrap *everything related* in one giant class, make the 'global' a member variable, and encapsulate it a bit from other code.

There are probably other tricks too.
@jonnin
Thanks I will keep that in mind for my work, the more faisable or least annoying method for now, I guess, would be to make a class with all static public member variables.

@tpb
Also I have a new issue. When defining an extern const int I get an error
expression did not evaluate to a const


Here is an example of what I have:
Settings.h:
1
2
3
4
5
6
#ifndef SETTINGS_H
#define SETTINGS_H

extern const int NUMBER;

#endif 

Settings.cpp:
1
2
3
#include "Settings.h"

const int NUMBER = 10;

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include "Settings.h"

using namesapce std;

int main()
{
    int array[NUMBER]; //ERROR!

    //[...]

    return 0;
}


1>main.cpp
1> \main.cpp(19): error C2131: expression did not evaluate to a constant
1> \main.cpp(19): note: failure was caused by non-constant arguments or reference to a non-constant symbol
1> \main.cpp(19): note: see usage of 'NUMBER'


Am I doing anything wrong, or am I missing something?
A const variable is a different thing entirely.

In the first place, they don't need to be extern at all since it doesn't matter if each compilation unit gets it's own "copy" (it would normally disappear from the code, anyway, since it isn't really a variable).

But to keep with the global Settings struct theme, just do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
// settings.h
#ifndef SETTINGS_H_
#define SETTINGS_H_

struct Settings {
    static int a;
    static int b;
    static const int c = 123;   // initialize it here
};

extern Settings settings;

#endif 


Actually, you should be able to initialize it here, instead (but you shouldn't do it in both places).
1
2
3
4
5
6
// settings.cpp
#include "settings.h"

int Settings::a = 1;
int Settings::b = 2;
const int Settings::c = 321;


And now that I think about it, I'm not sure why your example doesn't work. I'll try it myself....
Yeah, your example works fine for me (except of course for the misspelled "namespace").
Last edited on
closed account (E0p9LyTq)
You are trying to modify a const object by assigning a value to it after being declared as const.

1
2
3
4
5
6
7
8
#ifndef SETTINGS_H
#define SETTINGS_H

void PrintOut();

const int NUMBER = 10;

#endif 

1
2
3
4
5
6
7
#include <iostream>
#include "settings.h"

void PrintOut()
{
   std::cout << "In Settings.cpp, NUMBER: " << NUMBER << '\n';
}

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include "Settings.h"

int main()
{
   int array[NUMBER] = { 0 };

   std::cout << "In main, NUMBER: " << NUMBER << '\n';

   PrintOut();
}

In main, NUMBER: 10
In Settings.cpp, NUMBER: 10
You are trying to modify a const object by assigning a value to it after being declared as const.

That's what I was thinking at first, but the "extern" keyword changes the situation.
Try his example. It works just fine.
Last edited on
closed account (E0p9LyTq)
That's what I was thinking at first, but the "extern" keyword changes the situation.


const variables have to be assigned a value at creation, not after.
const variables have to be assigned a value at creation, not after.

But an extern doesn't create a variable.
Did you try his example?
It works for me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Settings.h
#ifndef SETTINGS_H
#define SETTINGS_H

extern const int NUMBER;

#endif

// Settings.cpp
#include "Settings.h"

const int NUMBER = 10;

// main.cpp
#include <iostream>
#include "Settings.h"

int main()
{
    int a[NUMBER];
    for (int i=0; i<NUMBER; i++)
        a[i] = i;
    for (int i=0; i<NUMBER; i++)
        std::cout << a[i] << ' ';
    std::cout << '\n';
}


$ g++ -std=c++11 -c main.cpp
$ g++ -std=c++11 -c Settings.cpp
$ g++ -std=c++11 main.o Settings.o
$ ./a.out
0 1 2 3 4 5 6 7 8 9 


Last edited on
closed account (E0p9LyTq)
Both Visual Studio and TDM-GCC don't compile your code, NUMBER being NON-const.

Your code is not ISO standard.
Intesting. Thanks for testing it. When I add -Wall -Wextra -pedantic it complains about a VLA, but that's it. Still, if it was working properly, it shouldn't complain about a VLA, so there's definitely something wrong with it.

But since an extern declaration is not a definition, I don't see why it should matter. As a different example, you can of course do this:
1
2
3
4
5
struct A {
    static const int n;  // no initializer here
};

const int A::n = 123;  // done here instead 

closed account (E0p9LyTq)
Try using the -Werror switch, make all warnings into errors along with the other switches. VS does most of that by default.

Still, if it was working properly

Non-standard code isn't portable, and compiling VLAs is non-standard.
Pages: 12