单例模式也称为单件模式、单子模式。单例模式有许多种实现方法,甚至可以直接用一个全局变量做到这一点,但这样的代码显得很不优雅。《设计模式》一书中给出了一种很不错的实现。
定义:确保一个类只有一个实例,而且自行实例化,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
类型:创建类模式
类图:
单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:
- 私有的构造方法
- 指向自己实例的私有静态引用
- 以自己实例为返回值的静态的公有的方法
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。代码如下:
饿汉式单例:
class CSingleton {
public:
static CSingleton* getInstance(){
return m_pInstance;
}
private:
CSingleton(){}
static CSingleton* m_pInstance;
};
CSingleton* CSingleton::m_pInstance= new CSingleton;
懒汉式单例:
class CSingleton {
public:
static CSingleton* getInstance(){
if(m_pInstance == NULL){
m_pInstance = new CSingleton();
}
return m_pInstance;
}
private:
CSingleton(){}
static CSingleton* m_pInstance;
};
CSingleton* CSingleton::m_pInstance = NULL;
单例模式需要注意的两个问题:
1.懒汉式单例模式线程安全问题:
遇到多线程问题时,假如现在有两个线程A和线程B,线程A执行到 this.singletonPattern = new SingletonPattern(),正在申请内存分配,可能需要0.001微秒,就在这0.001微秒之内,线程B执行到if(this.singletonPattern == null),你说这个时候这个判断条件是true还是false?是true,那然后呢?线程B也往下走,于是乎就在内存中就有两个SingletonPattern的实例了,看看是不是出问题了?所以,在编写懒汉式单例模式时,应注意线程安全问题,具体方法视个人习惯而定。
2.资源回收:
有经验的读者可能会问,m_pInstance指向的空间什么时候释放呢?我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。
利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人):
class CGarbo {
public:
~CGarbo()
{
if (CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo;
单例模式的优点:
- 在内存中只有一个对象,节省内存空间。
- 避免频繁的创建销毁对象,可以提高性能。
- 避免对共享资源的多重占用。
- 可以全局访问。
适用场景:由于单例模式的以上优点,所以是编程中用的比较多的一种设计模式。我总结了一下我所知道的适合使用单例模式的场景:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
- 以及其他我没用过的所有要求只有一个对象的场景。
至此为止,不考虑线安全问题,完整的单例模式代码如下:
饿汉式单例完整代码:
class CSingleton {
public:
static CSingleton* getInstance(){
return m_pInstance;
}
private:
CSingleton(){}
static CSingleton* m_pInstance;
class CGarbo {
public:
~CGarbo()
{
if (CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo;
};
CSingleton* CSingleton::m_pInstance= new CSingleton;
懒汉式单例完整代码:
class CSingleton {
public:
static CSingleton* getInstance(){
if(m_pInstance == NULL){
m_pInstance = new CSingleton();
}
return m_pInstance;
}
private:
CSingleton(){}
static CSingleton* m_pInstance;
class CGarbo {
public:
~CGarbo()
{
if (CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo;
};
CSingleton* CSingleton::m_pInstance = NULL;