[ Team LiB ] Previous Section Next Section

Gotcha #3: Global Variables

There is rarely an excuse for declaring a "raw" global variable. Global variables impede code reuse and make code hard to maintain. They impede reuse because any code that refers to a global variable is coupled to it and may not be reused without being accompanied by the global variable. They make code hard to maintain because it's difficult to determine what code is using a particular global variable, since any code at all has access to it.

Global variables increase coupling among components, because they often end up as a kind of primitive message-passing mechanism. Even if global variables work, it's often a practical impossibility to remove them from a large piece of software. If they work. Because global variables are essentially unprotected, any novice maintainer can trash the behavior of your global-dependent software at any time.

Users of global variables often cite convenience as a reason for using them. This is a fallacious or self-serving argument, because maintenance typically consumes more time than initial development, and use of global variables impedes maintenance. Suppose we have a system that requires access to a globally accessible "environment," of which (we're promised by our requirements) there is always exactly one. Unfortunately, we choose to use a global variable:



extern Environment * const theEnv; 


Requirements live but to lie. Shortly before delivery, we'll find that the number of possible, simultaneous environments has increased to two. Or maybe three. Or maybe the number is set on startup. Or is totally dynamic. The usual last-minute change. In a large project with meticulous source-control procedures in place, it can be a time-consuming process to change every file, even in a minimal and straightforward manner. It could take days or weeks. If we had avoided the use of a global variable, it would take five minutes:



Environment *theEnv(); 


Simply wrapping access in a function permits extension through the use of overloading or default argument initialization without the necessity of significant change to source code:



Environment *theEnv( EnvCode whichEnv = OFFICIAL ); 


Another, less obvious, problem with global variables is that they often require runtime static initialization. If a static variable's initial value can't be calculated at compile time, the initialization will take place at runtime, often with disastrous consequences (see Gotcha #55):



extern Environment * const theEnv = new OfficialEnv; 


If a function or class guards access to the global information, the setting of the initial value can be delayed until it's safe to do so:

gotcha03/environment.h



class Environment { 


 public:


   static Environment &instance();


   virtual void op1() = 0;


   // . . .


 protected:


   Environment();


   virtual ~Environment();


 private:


   static Environment *instance_;


   // . . .


};


gotcha03/environment.cpp



// . . . 


Environment *Environment::instance_ = 0;





Environment &Environment::instance() {


   if( !instance_ )


       instance_ = new OfficialEnv;


   return *instance_;


}


In this case, we've employed a simple implementation of the Singleton pattern to perform lazy "initialization" (actually, to be technically precise, it's assignment) of the static environment pointer and thereby ensure that there is never more than a single Environment object. Note that Environment has no public constructor, so users of Environment must go through the instance member to gain access to the static pointer, allowing us to delay creation of the Environment object until the first request for access:



Environment::instance().op1(); 


More important, this controlled access provides flexibility to adapt the Singleton to future requirements without affecting existing source code. Later, if we go to a multithreaded design or decide to permit multiple environments, or whatever, we can modify the implementation of the Singleton, just as we modified the wrapper function earlier.

Avoid global variables. Safer and more flexible mechanisms are available to achieve the same results.

    [ Team LiB ] Previous Section Next Section