Using global variables - really so bad?

Started by
19 comments, last by rip-off 10 years, 6 months ago

Hi
Ill give you an example.


g_SoundSystem;
class Game
{
  sf::RenderWindow screen;
  State* CurrentState;
public:
  void Start()
  {
     CurrentState->Logic();
  }
};

class Level1
{
  Player player;
public:
  void Logic()
  {
    if(something) g_SoungSystem->Play("shot.wav");
  }
}

CurrentState=new Level1();


I heard that global variables are bad.. but what is the other way to use things like "Soundsystems" etc. g_SoundSystem has to be global so i can use it from any place in the program. I was also thinking about setting flags in certain points but that would also require global access. In other words: Can you help me in understanding how to replace globals?

Advertisement

Do you really need the whole program to be able to play sounds? I would much rather have a "sound rendering" step in the game loop, where I decide what's going to be played and how, and this is the only part of the program that needs to know about the sound system.

If you do keep the sound system as a global, you may find that when you try to use something like the game logic of code in another program (for instance, the game logic could be used in a server in a multi-player game, or perhaps we are writing unit tests to make sure an algorithm improvement doesn't change results) you'll discover that you now need a sound system, a pseudo-random number generator, a logging system, a keyboard and a mouse for it to function, although perhaps the game logic shouldn't really need any of these things. Since you made all those things available everywhere, you have no guarantee that people are going to use them only from the places where it's appropriate to use them.

Don't use global variables, if you really want to use a variable alot, you can create it in your main and pass through to functions that need it.

Well, in a small project some global variable won't cause any problem, but you can avoid it and in a bigger project it's pretty unreadable.

Better forget global variables as early as possible and always searching the way to use every variable in the smallest scope that possible.

I learned in the past one simple answer to your question: it's much more readable!

of course it would be no problem to use global variables for any kind of problems but the simple thing is how bigger your program grow how bigger grow the amount of time you need to repair/add or change something in a proper way!

but if you cannot read your program properly e.g. After 2 years you will run crazy! The same is with singletons, in my eyes the better global variable but unfortunately still the same. In the end it is your decision, perhaps you have some serious reason to use them, then use it. You are free in your decision^^.

Really having a few globals hanging around is not so bad, especially if they are in their own namespace and there aren't *too many*.

So if you have a few "big objects" which you want global instances lying around, then use globals, it's not really a problem.

The problem really starts when:

* The maintenance programmer can't tell which variables are globals / members / locals etc, easily

* There is so much unstructured global state that it becomes easy to get "stale data" bugs.

In my limited experience, the larger the program, the more globals it tends to accumulate, and the harder it is to keep track of them, the more likely they'll be used by classes that shouldn't be using them, breaking that class's "self-contained" nature by making them rely on external variables, making the whole program more interdependent than it should be, making it harder to maintain and expand.

In small programs, this doesn't seem like a problem, but when I get into the habit of having globals just for the convenience of not doing it the more tedious but proper way, then if that small program ever grows large, or if you come to rely on that convenience and carry it over to larger projects, it might trouble you later.

A few key globals in a large project can be useful, but you have to keep your globals in check, because - due to laziness - mine tend to proliferate once I allow myself a few of them, and before I know it, three turns into twenty and then forty and more unless I get and keep them in check.

In a bid to fight them off, but still being lazy, the next poor step I usually accidentally take is by creating a class that pretends to not be global but really is a container for globals in disguise - like a singleton that holds the globals, or a class that holds a huge number of named "properties" (read: globals behind a fake mustache).

Non-private static members of classes are another way of pretending a global isn't a global and deceiving myself.

One of the hidden issues with globals is that globals across dynamic libraries can cause compiling problems depending on your compiler, and another problem is that globals (and static members) are initialized in an undefined order. The only guarantee you have is that they are constructed before main() enters, but if one is dependant on another, you don't have any guarantee which will be constructed first.

Though those issues can be worked around, doing so leaves me finding myself investing work into something that makes the code harder to maintain just to avoid writing proper code architecture in the first place (which I have difficulty designing properly, which is why I try to avoid it!).

Globals aren't evil, but they are super-easy to abuse.

I like a few constant values, enums, etc... as globals, but anything that could be named a 'system' is something that should be passed into a class's constructor or a function call by pointer. It means more coding work up-front, more fore-thought, more "ugh, why am I bothering to write these extra function arguments" moments, but a cleaner program architecture overall in the long-term.

...says the person who rewrites his programs several times because he didn't take the time to think it through and plan it properly.

An important question here is: does the global have a state that can be changed anywhere in the program? I suspect that a sound system would have state. Such globals are much more difficult to debug, because the problem can be anywhere in the code base, or even worse, the result of multiple lines of code that are widely separated.

It's much better to pass the object around where needed, and better still to have a sound rendering step as Alvaro mentioned, to restrict the scope of the object as much as possible.

Theoretically, (non-pointer) globals should be faster, because their fixed-addresses are established at compile-time. In contrast, local members or variables that are not declared static will usually be dynamically allocated on the stack or heap, so their memory addresses have to be calculated at run-time, when they are accesse. In the worst case, that calculation is compiled as a simple pointer+offset addition. Practically, the performance of a memory access is usually ruled by other factors than how the address is calculated, and a lot more performance can always be gained with higher-level optimizations than using globals. In fact, locals have an advantage when it comes to being cached by the CPU - since they are always "close" to the other local variables from their parent (stack or object), they have a lower chance of being discarded from the CPU data cache. If your program were to use only global variables, with no dynamic allocation at all, only then you would be able to say that the globals are faster, but nobody does that anymore. :) Plus, initialized globals will increase the size of your executable.

Theoretically, (non-pointer) globals should be faster, because their fixed-addresses are established at compile-time. In contrast, local members or variables that are not declared static will usually be dynamically allocated on the stack or heap, so their memory addresses have to be calculated at run-time, when they are accesse.

On x86/x64, the MOV instruction takes the same number of cycles regardless of the operand types, cache stalls non-withstanding.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Okay, I get why global variables can be bad, but I still don't know how to replace them. In my example it was easy to set correct sound to be played. Now let's say I don;t want to use the global SoundSystem. I could make SoundSystem member of a Game class. But how do I set the correct sounds now, when the "Player" class inside the State, which is already inside the game class, doesn't have access to the SoundSystem. It was easy to check.. if(playerState==state_attacking) g_SoundSystem->PlaySound("swordfling.wav");

Another idea was to pass the m_SoundSystem(member of the game class), to the State->Logic, and then to the Player.Logic() but that makes no sense to me. Well, at least I have never seen a source code/example with such a solution.

Or maybe I don't need anything like SoundSystem? Maybe simple function playing sounds is enough? However I find SoundSystem much more useful, as it can track which sounds are playing so I don't get the "100000 sounds at a time" effect etc.

Any tips from more advanced coders? ;)

This topic is closed to new replies.

Advertisement