C++实现单例模式
第一次写关于技术的文章,选择了一个单例模式这样的例子作为开头。用自己的理解去实现,希望能够表达清楚。
懒汉式
class Singleton
{
private:
Singleton(){}
static Singleton* instance;
public:
static Singleton* getInstance()
{
if(NULL == instance)
{
instance = new Singleton();
}
return instance;
}
};
//静态成员变量必须在实现文件中进行定义,而不仅仅是在头文件中声明
Singleton* Singleton::instance = NULL;
这个代码简单,但是会存在内存泄漏的问题,new出来的对象始终没有释放,下面是它的一种改进。
class Singleton
{
private:
Singleton() {}
static Singleton* m_pInstance;
//嵌套类
class CGrabo
{
public:
~CGrabo()
{
if(Singleton::m_pInstance)
{
delete Singleton::m_pInstance;
Singleton::m_pInstance = NULL;
}
}
};
static CGrabo Grabo;
public:
static Singleton* getInstance()
{
if(NULL == m_pInstance)
{
m_pInstance = new Singleton();
}
return m_pInstance;
}
};
说明:在程序运行结束的时候,静态成员变量Grabo的析构函数会被调用,该析构函数会删除单例中的唯一实例。其特点是在单例类的内部定义专有的内部类,并且定义该类的一个私有静态成员,利用静态变量的特性,选择最终的释放时机。
饿汉式
class Singleton
{
private:
Singleton() {}
public:
static Singleton * getInstance()
{
static Singleton instance;
return &instance;
}
};
饿汉模式实现简单并且是线程安全的,在类创建的同事就已经创建好一个静态的对象供系统使用。
懒汉模式在多线程中会出现线程安全的问题,因为在懒汉模式中单例实例有两种状态,分别是初始化和未初始化。假设单例实例还没有初始化,有两个线程同事调用getInstance方法,这是执行m_pInstance==NULL肯定为真,然后两个线程都初始化一个实例,最后得到的指针并不是指向同一个地方,不满足单例的定义了。在多线程环境下,要对其进行修改。
多线程下的单例模式
——————————— 懒汉模式
class Singleton
{
private:
static Singleton* m_pInstance;
Singleton(){}
//嵌套类
class CGrabo
{
public:
~CGrabo()
{
if(Singleton::m_pInstance)
{
delete Singleton::m_pInstance;
Singleton::m_pInstance = NULL;
}
}
};
static CGrabo Grabo;
public:
static Singleton* getInstance();
};
在实现类实现
Singleton* Singleton::getInstance()
{
if(NULL == m_pInstance)
{
Lock();//通过同步类来实现,如临界区,事件,互斥,信号量等
if(NULL == m_pInstance)
{
m_pInstance = new Singleton();
}
UnLock();
}
return m_pInstance;
}
这里为什么需要两次判断m_pInstance??这是为了防止一个线程进入临界区创建实例,另外的线程也进入临界区创建实例,所以加上第二个判断if(NULL==m_pInstance),确保不会重复创建。
如果是这样写是否也可以???
Singleton* Singleton::getInstance()
{
lock();
if(NULL == m_pInstance)
{
m_pInstance = new Singleton();
}
unlock();
return m_pInstance;
}
这样写的话,会稍微影响性能,因为每次判断是否为空都需要被锁定,如果有很多线程的话,就会造成大量线程阻塞。
既然如此,也会有人想能不能把同步锁放入到判断空里面,这样就不会每次都锁了,像这样:
Singleton* Singleton::getInstance()
{
if(NULL == m_pInstance)
{
lock();
m_pInstance = new Singleton();
unlock();
}
return m_pInstance;
}
这样就又回到了饿汉模式的非线程安全上去了。
这是总结的一些东西,或许并不是总结得很好,也会有很多自己没有想到的东西,或者还有更加巧妙的方法来改进,这里暂时就不再深究了。