Why are static variables bad?

Started by
56 comments, last by Sacaldur 10 years, 7 months ago

I've read a few threads here that people advise against static variables, and the argument is usually because anything global is bad, but besides this, why are static variables considered bad?

Do they perform badly or something like that?

I'm asking because I've got my engine structure with modules, and I'm using static variables for each module so my base classes can access it.

class GameEngine
{
    public:
        GameEngine()
        {
            resourceModule = &_resourceModule;
        };
 
        static ResourceModule* resourceModule;
    private:
        ResourceModule _resourceModule;
};

#include "GameEngine.h"
 
void Image::Load(string imageName)
{
    //Texture2D* _texture;
    _texture = GameEngine::resourceModule->GetLoadedTexture(imageName);
}

Images are meant to be used like this:

void SomeGameFunction()
{
    Image* newImage = new Image();
    newImage->Load("imageNameHere");
}

I'm currently doing some encapsulation and blocking accesses to base classes that I don't want exposed, so other programmers don't ever access a "Texture2D" class for example, and when I looked to these static variables I thought they might be an issue but I couldn't find of any good solutions for it, that is, if they require a solution...

So I remembered the topics against static variables I thought I should ask, why are static variables considered bad?

Advertisement

It's not that static variables are bad necessarily, but they are often misused and that can lead to code that is rather unpleasant to work with.

-Josh

--www.physicaluncertainty.com
--linkedin
--irc.freenode.net#gdnet

Static variables are fine at times. Do consider multithreading though, as they are inherently NOT thread safe...

As for the posted code... it has problems which highlight some dangers with your usage...

What if there is more than 1 game engine?

What if the user did not instantiate a game engine object before calling GameEngine::resourceModule-> anything? That static value could point to anything at all.

What if GameEngine is destroyed or goes out of scope? Our static variable is now pointing to invalid memory...

There are surely more issues here. So, a big issue with static variables is that they are hard to safely control....

[edit] Perhaps a good rule is not to use static variables just for perceived "convenience". Use them because they fit the problem... which you'll find is not very often.

You shouldnt use static variables if you dont really need them, they can introduce subtle bugs, especially with threading.

For example your GameEngine class is dangerous. If one of the unknown huge number of places in your code(because its globally accessible which is even worse than having a hidden static variable) accesses it before you created a GameEngine object your program most likely crashes through accessing a null pointer, same if its already destroyed or a second object is created and destroyed and you now access already freed and possibly overwritten memory.

You better try hard to avoid global variables and static variables and just use local variables and give any function all data it needs through its parameters.

Right. Static variables are bad when used incorrectly. Making static variables globally accessible invites the programmer to access that variable at any point in the code without any clear indication from outside that code that there is a dependency on that global variable. This makes it impossible to test or reuse software components as individual units. Incorrectly used global static variables also threaten to wreak havoc all over your codebase whenever you attempt to change the way the variable is used or what it means. The alternative to using global static variables is usually to implement form of dependency injection so that any unit that depends on the variable will have its dependency clearly and explicitly indicated. Often this also has the benefit of compile-time error checking -- if you forget to pass that variable to your constructor or method that depends on it, then it won't compile.

Another potential drawback to using bare statics is that they are not thread safe. Any thread can access and modify the static without any sort of locking.

Code tends to be more maintainable and have fewer ways to introduce bugs when there are fewer accessible variables from a given point of execution (of any kind, not just statics) and each variable is in the narrowest scope (local variables < ref arguments < closures <= instance members < thread-local storage < static members = global variables). Static variables have the widest possible scope and longest lifetime.

Static variables can pose serious problems for:

- Decoupling.

- Code reuse.

- Thread safety.

- Restarting the game without relaunching the executable.

Statics can be perfectly fine as long as you always keep those things in mind.

Essentially one of the more better know forms of improving your code is making it more modular, i.e. each section can exist without crashing even if another piece of code is not there that it requires to do anything interesting. That sort of "decomposition" of classes into little black boxes makes code simpler to track because it implies that each class can change it's members out or create multiple copies of objects without introducing bugs.

Static tends to go against that methodology because it implies there is only every one instantiation of a certain variable that is shared between everything. In a way it isn't necessarily a "global" but it sort of becomes this unnatural link of a permanent object that "belongs to everyone."

Usually something that belongs to everyone introduces things like bugs where state is changed across contexts or just complicates understanding which objects are accessing it.


So I remembered the topics against static variables I thought I should ask, why are static variables considered bad?

Static lifetime is hard to manage. Some static objects get initialized even before main() is called and in an order you cannot enforce. Static objects are not destroyed until long after main() has finished.

Static variables introduce hidden dependencies between objects.

Static variables have unenforced ownership semantics. Who modifies the variable and when?

Static variables can be a nightmare for automated testing. They introduce subtle (and not-so-subtle) behavior that can invalidate tests.

Your original example has conflated the use of static (or class) variables and public access to data. The two topics are unrelated and orthogonal.

There's nothing at all wrong with using a class variable, where appropriate.

There are serious problems with using a publicly accessible variable of static storage duration, whether at namespace level or class level (these are known as 'global variables' in the C language and as a consequence colloquially also in the C++ language).

The biggest problem with the original example code is the unnecessary coupling between the GameEngine class and the Image class. With that design, not only is the coupling brittle (ie. if i change the GameEngine, I have to change the Image) but the design prevents me from reusing the Image class in, say, a game editor or image editor that does not need a GameEngine. Writing unit tests for your Image is next to impossible. None of these problem have anything to do with the storage duration of your data but are a direct result of the accessibility of your data.

Stephen M. Webb
Professional Free Software Developer


I'm asking because I've got my engine structure with modules, and I'm using static variables for each module so my base classes can access it.

In addition to what the others have said, this is an inevitable bad design you are applying here. Having your modules globally accessible might seem tempting at first, but it will inevitably lead to a pile of cluttered spaghetti code. You might think "well, if I stop doing so I might end up having to pass my modules to everything and every constructor will have like 5 arguments just because of this", but this is not as it seems. You can end up in such a situation, but the main benefit from making your modules non-global are the you'll have to think about a clever design that will avoid you having to pass like the graphics-module 10 layers down into 100 different classes. This will force you to try to decouple your classes as much as possible, enforce proper encapsulation, etc, etc. In the end, you'll end up with much more usable classes, without hidden dependencies, and, I predict you, you'll actually save more work than you'd have if you made all those global.

And, since the others have already given a proper shot at the original question, and since I'm on it, here are some more hints regarding your overall design (only read on if you want some feedback over your original thought process):


GameEngine::resourceModule->GetLoadedTexture(imageName);

While I do not see the rest of the class, it appears the resourceModule has too much responsibility. I suppose it has methods for loading and creating textures, meshes, etc... and will do so on? That violates the single responsibility principle, and while this and other rules aren't necessary important for you to follow right now, its never too early to start using them. In short, you are better of splitting the different resource caches, and also split the loading functionality into its own class.


Image* newImage = new Image();

newImage->Load("imageNameHere");

Also, I'd rather split the loading-method from the image class. Such a design like here makes it hard to extend on things, if you have a seperate loader class its easier to add new loading methods, without breaking apart the other code.

Just some additional thoughts, if you ever feel the need to improve something or feel like your current design isn't working anymore as you'd expected...

This topic is closed to new replies.

Advertisement