方式一、
class Singleton
{
public:
static Singleton & instance()
{
return(s_instance);
}
private:
~Singleton()
{
}
static Singleton s_instance;
};
Singleton Singleton::s_instance;
点评:“如果有SingletionA和SingletonB,在SingletonA的构造函数中使用SingletonB的实例,由于C++标准并没有明确说明静态变量的初始化顺序,所以有可能造成此时SingletonB还没有初始化的情况,而引发错误。”
方式二、
class Singleton
{
public:
static Singleton & instance()
{
static Singleton s_instance;
return(s_instance);
}
private:
~Singleton()
{
}
};
点评:“Singleton的构造函数只会在第一次调用instance()时被初始化, 缺点是在多线程环境下会出问题。”
方式三、
class Locker;
extern Locker locker;
class Singleton
{
public:
static volatile Singleton * instance()
{
if (nullptr == s_instance)
{
locker.acquire();
if (nullptr == s_instance)
{
s_instance = new Singleton;
}
locker.release();
}
return(s_instance);
}
private:
~Singleton()
{
}
static volatile Singleton * s_instance;
};
volatile Singleton * Singleton::s_instance = nullptr;
点评:“如果上面代码没有volatile关键字,则编译器的优化可能会带来问题:比如locker.acquire()后面的s_instance的值不是从&s_instance处取的(假设此时已经非空),而是直接取自寄存器之前保留的值(假设寄存器的保存值还是空),就有问题了;这只是一种可能,还可能别处有问题。请参考volatile的含义、作用"
方式四、
template <typename T>
class Singleton
{
public:
struct object_creator
{
object_creator()
{
Singleton<T>::instance();
}
inline void do_nothing() const { }
};
static object_creator create_object;
static T & instance()
{
static T obj;
create_object.do_nothing();
return obj;
}
// 下面这句代码是我加的,显然它是有问题的,
// 但是在VS编译器下,由于没有代码引导此函数实例化,
// 所以这块代码不会被编译,也就不会发现有错误了。
// int ABC() { my life is sucks! }
};
template <typename T>
typename Singleton<T>::object_creator Singleton<T>::create_object;
目前boost库中Singleton就采用这种方式实现。此处的Singleton不仅要解决多线程下Singleton的问题,还要避免使用线程锁,只用C++本身的特型来实现多线程下的Singleton类;它是意图在开启多线程之前就构建出Singleton,并假设用户代码都是在main之后才会开启多线程 (所以,它通过将Singleton在main之前实例化,再依据之前的假设,意味着实现了在多线程开启前将Singleton实例化)
这点代码可能不太好理解,下面加上测试代码:
class A
{
public:
A()
{
}
};
class B
{
public:
B()
{
Singleton<A>::instance();
}
};
int main()
{
return(0);
}
解析:
虽然,代码中没有明确定义任何对象,但是B()中的“Singleton<A>::instance()”语句,导致“Singleton<A>::instance()”被实例化,接着Singleton<A>::instance()中的“create_object.do_nothing();”,导致了代码“static object_creator create_object;”和“do_nothing()”实例化,而代码“static object_creator create_object;”导致了“object_creator()”被实例化,这也使得对象create_object被创建 (在main前)
去除instance()中的“create_object.do_nothing();”,就不会推导出任何与struct object_creator有关的代码,也就不会有create_object产生,也就不能保证instance()中的obj在main之前就产生,从而出现方式二中的多线程问题,
这就是“create_object.do_nothing();”的作用:帮助引导create_object实例化 (“create_object.do_nothing();” 改成 “create_object;” 也是可以达到目的的)
参考文章:
http://leoxiang.com/dev/different-cpp-singleton-implementaion