I keep hearing that global variables should never be used, but I have a tendency to dismiss \"never\" rules as hot-headed. Ar
The reason i never do it is because it creates a mess. Imagine setting ALL unique variables to globals, you would have an external list the size of a phonebook.
Another reason could be that you don't know where it is initialized or modified. What if you accidently modify it at place X in file Y? You will never know. What if it isn't initialized yet? You will have to check everytime.
if (global_var = 0) // uh oh :-(
if (object->Instance() = 0) // compile error :-)
This can both be fixed using singletons. You simply cant assign to a function returning you the object's adress.
Besides that: you don't need your screen buffer everywhere in your application, however if you want to: go ahead, it doesn't make the program run less good :-)
And then you still have the namespace problem but that at least gives you compile errors ;-)
Of course there are exceptions. I personally can't think of a single situation where a goto is the right solution (or where a singleton is the right solution), but global variables occasionally have their uses. But... you haven't found a valid excuse.
Most objects in your game do not, repeat, not need to access the screen buffer. That is the responsibility of the renderer and no one else. You don't want your logger, input manager, AI or anyone else putting random garbage on the screen.
And that is why people say "don't use globals". It's not because globals are some kind of ultimate evil, but because if we don't say this, people fall into the trap you're in, of "yeah but that rule doesn't apply to me, right? I need everything to have access to X". No, you need to learn to structure your program.
More common exceptions are for state-less or static objects, like a logger, or perhaps your app's configuration: things that are either read-only or write-only, and which truly needs to be accessible from everywhere. Every line of code may potentially need to write a log message. So a logger is a fair candidate for making global. But 99% of your code should not even need to know that a screen buffer exists.
The problem with globals is, in a nutshell, that they violate encapsulation: Code that depends on a global is less reusable. I can take the exact same class you're using, put it in my app, and it'll break. Because I don't have the same network of global objects that it depends on.
It also makes the code harder to reason about. What value will a function f(x)
return?
It obviously depends on what x
is. But if I pass the same x
twice, will I get the same result? If it uses a lot of globals, then probably not. Then it becomes really difficult to just figure out what it's going to return, and also what else it is going to do. Is it going to set some global variable that's going to affect other, seemingly unrelated, functions?
How can this be achieved, preferably without having to pass it to every constructor to be stored internally until needed
You make it sound like that's a bad thing. If an object needs to know about the screen buffer, then you should give it the screen buffer. Either in the constructor, or in a later call. (And it has a nice bonus: it alerts you if your design is sloppy. If you have 500 classes that need to use the screen buffer, then you have to pass it to 500 constructors. That's painful, and so it's a wake-up call: I am doing something wrong. That many object shouldn't need to know about the screen buffer. How can I fix this?`)
As a more obvious example, say I want to calculate the cosine of 1.42, so I pass 1.42 to the function: cos(1.42)
That's how we usually do it, with no globals. Of course, we could instead say "yeah but everyone needs to be able to set the argument to cos
, I'd better make it global". Then it'd look like this:
gVal = 1.42;
cos();
I don't know about you, but I think the first version was more readable.
We tell students never to use global variables because it encourages better programming methods. It's the same reason we tell them not to use a goto statement. Once you're an accomplished programmer then you can break the rules because you should know when it's appropriate.
What if you want to update your engine to support dual screen? Multiple displays are becoming more and more common all the time. Or what if you want to introduce threading? Bang. How about if you want to support more than one rendering subsystem? Whoopsie. I want to pack my code as a library for other people or myself to re-use? Crap.
Another problem is that the order of global init between source files is undefined, making it tricky to maintain more than a couple.
Ultimately, you should have one and only one object that can work with the screen buffer - the rendering object. Thus, the screen buffer pointer should be part of that object.
I agree with you from a fundamental point of view - "never" is inaccurate. Every function call you make is calling a global variable - the address of that function. This is especially true for imported functions like OS functions. There are other things that you simply cannot unglobal, even if you wanted to - like the heap. However, this is most assuredly not the right place to use a global.
The biggest problem with globals is that if you later decide that a global wasn't the right thing to do for any reason (and there are many reasons), then they're absolutely hell to factor out of an existing progam. The simple fact is that using a global is just not thinking. I can't be bothered to design an actual rendering subsystem and object, so I'm just gonna chuck this stuff in a global. It's easy, it's simple, and not doing this was the biggest revolution in software programming, ever, and for good reason.
Make a rendering class. Put the pointer in there. Use a member function. Problem solved.
Edit: I re-read your OP. The problem here is that you've split your responsibilities. Each class (bitmap, text, whatever) should NOT render itself. It should just hold the data that the master rendering object needs to render it. It's a Bitmap's job to represent a bitmap - not to render a bitmap.
"Why not": global variables give you spaghetti information flow.
That's the same as goto gives you spaghetti control flow.
You don't know where anything comes from, or what can be assumed at any point. The INTERCAL solution of introducing a come from statement, while offering some initial hope of finally being sure of where control comes from, turned out to not really solve that problem for goto
. Similarly, more modern language features for tracking updates to global variables, like onchangeby
, have not turned out to solve that problem for global variables.
Cheers & hth.,