单例模式有多种写法, 最简单的一种是:
class Test
{
private:
Test() {}
private:
static Test* instance;
public:
static Test* GetInstance()
{
if (!Test)
{
instance = new Test;
}
return instance;
}
};
但是这种形式,在多线程中使用可能出错,在还没创建对象,多个线程同时调用GetInstance()时,假设线程1执行了
if (!Test)
这时切换线程了,cpu去执行线程2的 if (!Test) , 线程2创建对象 instance = new Test;
然后cpu回来执行线程1的创建对象 instance = new Test;
这样 new 操作可能执行了多次,创建了多个实例。
总结:虽然出错几率很小,但还是有,只发生在第一次创建实例时,一旦创建实例成功,就再也不会出错了。
第二种:
class Test
{
private:
Test() {}
public:
static Test& GetInstance()
{
static Test instance;
return instance;
}
};
这种方式很棒, 像下面这种使用方法,很完美:
Test::GetInstance().Start();
创建一个类的实例,并启动这个模块。
但这只适用于一个通过Start()函数启动的模块,并且调用Start()之后不再需要调用任何成员函数的情况。
如果调用Start()之后,还需要调用一个叫做abc的函数呢?
如果这样使用没有错:
Test::GetInstance().Start();
Test::GetInstance().abc();
正确
但是,恐怕,一般人会这样用:
Test test = Test::GetInstance();
test.Start();
test.abc();
错误
错误在第一句,这是一个赋值操作,= 左面是一个对象, =右边是另一个对象, 右边的给左边的赋值,但是这是两个对象啊,test早已经不是那个单例的对象了。
总结: 你能保证模块的使用者肯定是上边的用法吗 ,如果不能保证,这种单例模式就不好。
第三种:
这是第一种的改进, 第一种的缺陷是在第一次创建实例时有可能出错,我们可以避免这种情况:
在模块所在的.h文件中创建实例,只要用户包含了该头文件,实例就已经存在了,他再怎么创建也不是第一次了,代码如下
class Test
{
private:
Test() {}
private:
static Test* instance;
public:
static Test* GetInstance()
{
if (!Test)
{
instance = new Test;
}
return instance;
}
};
Test *ptest = Test::GetInstance();
第四种:
照第三种这样的,也可以吧第二种改造一下喽,先创建对象,让用户用。
class Test
{
private:
Test() {}
public:
static Test& GetInstance()
{
static Test instance;
return instance;
}
};
#define sTest Test::GetInstance()
让用户用这个宏sTest当对象使用就行了,很不错。
到了这一步,就发现(13)和(24)这两种方式都差不多,都终都是在.h文件中首先实例化一个对象。
第五种:
to be continue ... ....