Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is lazy initialization really possible with static data members?

Is it possible to initialize the Instance of a Singleton when it is really needed?

Consider this pattern taken from the famous "Design Patterns":

class Singleton {
public:
   static Singleton* Instance();
protected:
   Singleton();
private:
   static Singleton* _instance;
}

Singleton* Singleton::_instance = 0; // unit.cpp

static Singleton* Singleton::Instance() {
   if (_instance == 0) {
       _instance = new Singleton;
   }
   return _instance;
}

Now, I think there is a problem in the pattern there, if one desires to provide the Singleton in a library to others: if the user calls Singleton::Instance() from another compilation unit (during a static data member initialization, for example) before _instance is initialized, then a following call of Singleton::Instance() might create another instance of the Singleton, with unwanted results, since _instance might have been initialized to 0 first.

I think one solution is to initialize _instance this way:

Singleton* Singleton::_instance = Singleton::Instance();

Anyway, that makes the initialization not "lazy" for those who don't need to call Singleton::Instance() to initialize their static data.

Are there better solutions so that the inizialization can happen when the Singleton instance is needed?

like image 843
pipex Avatar asked Dec 12 '22 10:12

pipex


1 Answers

Static initialization happens before dynamic initialization

The singleton solution you have quoted works because

Singleton* Singleton::_instance = 0;

describes a static initialization (namely zero initialization in this case, but a constant initialization would work as well if used), which is guaranteed to happen before any dynamic initialization (and therefore before any code is run). All requirements of static initialization are guaranteed here, as _instance is a global variable of a built-in type (a pointer) and initialized with zero.

The other solution you have provided does not change anything significant, because still only the zero initialization of _instance is guaranteed to happen before code from other modules is called, as the initialization by Singleton::Instance call is a dynamic one and therefore is subject to static initialization order fiasco.

Implementation - data segment

Note: Static initialization is most often implemented by storing the value of the variable, which was computed compile time, in a data segment of the executable.

Terminology, standard quote

While most programmers (including Bjarne Stroustrup) call the initialization style used in the original singleton implemementation "compile time initialization", the standard calls it "static initialization", as opposed to a "dynamic initialization" (which is what is most often called run-time). See C++0x draft 3.6.2 (shortened, emphasis mine):

3.6.2 Initialization of non-local variables [basic.start.init]

... Non-local variables with static storage duration are initialized as a consequence of program initiation. ... as follows.

2 Variables with static storage duration (3.7.1) ... shall be zero-initialized (8.5) before any other initialization takes place.

Constant initialization is performed: ...

  • if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.

Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.

like image 168
Suma Avatar answered Feb 01 '23 22:02

Suma