effectie c++的条款4中提到:
(global对象,定义在namespace内的对象,class内的static对象,函数内的static对象,file作用域内的static对象)统称为static对象。其中函数内的static对象又叫local static object, 其他的叫non-local static object。
non-local static object的初始化顺序是没有定义的,local static object在函数第一次调用时构造初始化。
还有:non-local static object会在main函数之前被初始化。
#pragma once
#include <iostream>
using namespace std;
class Foo
{
public:
Foo()
{
cout<<"Foo create!"<<endl;
}
};
class Test
{
public:
Test() {}
Foo GetX() const{ return x_;}
private:
static Foo x_;
};
Foo Test::x_;
即使在main函数中未初始化Test对象,仍会看到Foo Create的提示,所以non-local static object在main函数之前初始化的。
普通的singleton模式:
#pragma once
template<typename T>
class Singleton_
{
public:
static T&Instance()
{
static T t_;
return t_;
}
private:
Singleton_() {}
};
多线程的时候此方法不给力,可以用加锁的办法,参见ACE实现的双重加锁优化的singleton实现。
由于non-local static object是在main之前初始化的,默认进入main函数之前只有主线程运行,则有如下写法,犯了对template不熟悉的错误。
#pragma once
template<typename T>
class Singleton_
{
public:
static T&Instance()
{
return t_;
}
static T t_;
private:
Singleton_() {}
};
T Singleton_<T>::t_;
这样定义编译不通过的!因为模板是编译期的多态,编译器用真实的类型替换T来生成相应的代码。
上述类中的T在main之前构造,编译器无法推导出其真实的类型。
下面看boost的singleton实现:
class Widget
{
public:
Widget()
{
cout<<"Widget Creat"<<endl;
}
};
template <typename T>
struct Singleton
{
struct object_creator
{
object_creator()
{
Singleton<T>::instance();
}
inline void do_nothing()const
{}
};
static object_creator create_object;
public:
typedef T object_type;
static object_type& instance()
{
static object_type obj;
create_object.do_nothing();
return obj;
}
};
//声明一个全局变量template <typename T> Singleton<T>::create_object
typename Singleton<T>::object_creator Singleton<T>::create_object;
int main()
{
Widget& w = Singleton<Widget>::instance();
return 0;
}
没有使用锁机制,而是充分利用了C++的语言特性较好的解决了多线程情况下使用singleton的问题。
boost的singleton的实现基于以下假设:良好的设计在进入main函数之前应该是单线程的。
我们可以使用全局变量的方式来设计singleton,并且保证在使用该singleton之前其已经被正确的初始化。
在进入main之前,唯一的主线程开始构造Singleton<T>::create_object,在其构造函数之内调用Singleton的instance函数,并在该函数内生成Singleton对象,至于函数donoting(),去掉之后照样可以通过编译,我想原因可能是为了再次保证singleton的初始化完全成功。