Is lazy initialization really possible with static

2019-05-10 04:34发布

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?

3条回答
爷、活的狠高调
2楼-- · 2019-05-10 04:56

use local statics

Singleton* Singleton::getInstance()
{
    static Singleton obj; return &Singleton;
}
查看更多
走好不送
3楼-- · 2019-05-10 05:06

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.

查看更多
Ridiculous、
4楼-- · 2019-05-10 05:08

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.

No problem: static variables are zero-initialized before any dynamic initialization takes place.

On the other hand, the code you then introduce as solution to non-existent problem, namely

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

is problematic – no guarantee about order of (dynamic) initialization between translation unit.

Cheers & hth.,

查看更多
登录 后发表回答