单例的实现有很多的坑,并不是简单的一个static的成员获取就算是单例了,下面详细叙述下它的坑。
-. 懒汉模式
class singleton
{
private;
singleton();
public:
~singleton();
static singleton getInstance();
{
static singleton minstance;
return minstance;
}
};
懒汉模式在一般使用下都不会有问题,但是,这个实现是线程不安全的。局部静态变量static singleton minstance ,编译器会在编译时期对其做处理,其结果如下:
static bool s_constructed = false;
static uninitialized singleton minstance;
if (!s_constructed) {
s_constructed = true;
new(&minstance) minstance; // construct it
}
从这里可以显而易见到存在线程安全的问题。那对于这种情况怎么处理呢? 最简单的就是实用恶汉模式。
二. 饿汉模式
我们知道 , 全局成员,类静态成员,都是在main函数执行前进行初始化的,而局部静态成员是在运行到它的时候才进行初始化,所以饿汉模式就是在程序还没有运行的时候,就对单例进行初始化。
class singleton
{
private;
singleton();
static singleton minstance;
public:
~singleton();
static singleton getInstance();
{
return minstance;
}
};
singleton singleton::minstance;
这样,就不会存在编译器对于懒汉模式所做的处理了,在main函数执行前,singleton对象就已经够造了出来,这样就是一个线程安全的单例模式了。
以为这就是单例模式最终的形态了吗? too young too simple!!!!!
参考了boost的单例模式实现:
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;
//据说这个do_nothing是确保create_object构造函数被调用
//这跟模板的编译有关
create_object.do_nothing();
return obj;
}
};
template <typename T> typename Singleton<T>::object_creator Singleton<T>::create_object;
发现有这么一个实现,将单例对象还是做程局部静态变量,但是通过另一个内部类object_creator代替其在全局区声明初始化,这是什么原因呢?
参考了一位大牛的blog,发现还有这么一种2b的用法:
//.h
class QMManager
{
protected:
static QMManager instance_;
QMManager();
~QMManager(){};
public:
static QMManager *instance()
{
return &instance_;
}
};
class QMSqlite
{
protected:
static QMSqlite instance_;
QMSqlite();
~QMSqlite(){};
public:
static QMSqlite *instance()
{
return &instance_;
}
void do_something();
};
QMManager QMManager::instance_;
QMSqlite QMSqlite::instance_;
//.cpp
QMManager::QMManager()
{
printf("QMManager constructor\n");
QMSqlite::instance()->do_something();
}
QMSqlite::QMSqlite()
{
printf("QMSqlite constructor\n");
}
void QMSqlite::do_something()
{
printf("QMSqlite do_something\n");
}
就是在构造类静态成员的时候,它的构造函数有可能去调用另一个单例模式的静态成员,以便调用其方法。可以发现,这是存在问题的,在执行main函数之前,两个静态成员QManager和QMsSqlite的构造是有先后顺序的,QManager先进行的初始化,这时候,当调用QManager的构造函数的时候,去主动获取QMSqlite的单例,并调用其成员,但是,这时候QMSqlite的单例对象instance_还没有初始化,就是说,现在调用的是一个为初始化的变量,这当然存在问题了。 解决的办法就是,将这两个变量的初始化工作放在局部变量,让程序运行到的时候去初始化。但是这样就又会出现懒汉模式中的线程不安全的问题,所以,讨巧的做法就是把他们封装进另一个静态成员的构造中。
最终的单例模式:
class singleton
{
private:
singleton();
class create_object
{
public:
create_object(){
singleton::getInstance();
}
};
static create_object m_createobj;
public:
~singleton();
static singleton& getInstance();
void eat()
{
printf("fuck\n");
}
};
singleton::create_object singleton::m_createobj;
singleton::singleton()
{
}
singleton::~singleton()
{
}
singleton& singleton::getInstance()
{
static singleton m_instance;
return m_instance;
}
小小的单例模式,有这么多的坑
贴一段模仿boost的实现:
#include "stdafx.h"
#include <iostream>
using namespace std;
class signletonSt
{
private:
signletonSt(){}
~signletonSt(){}
struct create_signletonst
{
create_signletonst()
{
signletonSt::getInstance();
}
};
static create_signletonst m_creator;
public:
static signletonSt* getInstance()
{
static signletonSt* m_st = NULL;
if (!m_st)
{
cout << "first new" << endl;
m_st = new signletonSt();
}
else
{
cout << "has instanced" << endl;
return m_st;
}
}
};
signletonSt::create_signletonst signletonSt::m_creator;
int _tmain(int argc, _TCHAR* argv[])
{
signletonSt* m1 = signletonSt::getInstance();
return 0;
}