local static
上面一种写法只能在进入main函数后才能调用getInstance,那么有人说,我要在main函数之前调用怎么办?
嗯,办法还是有的。这个时候我们就可以利用local static来实现,C++标准保证函数内的local static变量在函数调用之前被初始化构造完成,利用这一特性就可以达到目的:
template<typename T> class Singleton { private: Singleton(); ~Singleton(); class Creater { public: Creater() : value_(new T()) { } ~Creater() { delete value_; value_ = NULL; } T& getValue() { return *value_; } T* value_; }; public: static T& getInstance() { static Creater creater; return creater.getValue(); } private: class Dummy { public: Dummy() { Singleton<T>::getInstance(); } }; static Dummy dummy_; }; template<typename T> typename Singleton<T>::Dummy Singleton<T>::dummy_; |
这样就可以了。dummy_的作用是即使在main函数之前没有调用getInstance,它依然会作为最后一道屏障保证在进入main函数之前构造完成Singleton对象。这样就避免了在进入main函数后的多线程环境中初始化的各种问题了。
但是此种方法只能在main函数执行之前的环境是单线程的环境下才能正确工作。
实际上,上文所讲述了各种写法中,有一些不能在main函数之前调用。有一些可以在main函数之前调用,但是必须在进入main之前的环境是单线程的情况下才能正常工作。具体哪种写法是属于这两种情况就不一一分析了。总之,个人建议最好不要在进入main函数之前获取Singleton对象。因为上文中的各种方法都用到了staitc member,而C++标准只保证static member在进入main函数之前初始化,但是不同编译单元之间的static member的初始化顺序却是未定义的, 所以如果在main之前就调用getInstance的话,就有可能出现实现Singleton的static member还没有初始化就被使用的情况。
如果万一要在main之前获取Singleton对象,并且进入main之前的环境是多线程环境,这种情形下,还能保证正常工作的写法只有C++ 11下的Meyers Singleton,或者如g++ 4.0及其后续版本这样的编译器提前支持内存模型情况下的C++ 03也是可以的。