出发点
保证一个类仅有一个实例,并提供一个该实例的全局访问点。 ——《设计模式》GoF
实现
单例分为两种实现方法:
-
懒汉
- 第一次用到类实例的时候才会去实例化,上述就是懒汉实现。
-
饿汉
- 单例类定义的时候就进行了实例化。
懒汉模式的实现:
class Singleton{
public:
static Singleton* getInstance(){
// 先检查对象是否存在
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
private:
Singleton(); //私有构造函数,不允许使用者自己生成对象
//关键点就是将Singleton()设置为私有的
Singleton(const Singleton& other);
static Singleton* m_instance; //静态成员变量
};
Singleton* Singleton::m_instance=nullptr; //静态成员需要先类外初始化
饿汉的实现:
class singleton {
private:
singleton() {}
static singleton *p;
public:
static singleton *instance();
};
singleton *singleton::p = new singleton();
singleton* singleton::instance() {
return p;
}
解释
这是一个非常简单的实现,将构造函数声明为private或protect防止被外部函数实例化,内部有一个静态的类指针保存唯一的实例,实例的实现由一个public方法来实现,该方法返回该类的唯一实例。
当然这个代码只适合在单线程下,当多线程时,是不安全的。考虑两个线程同时首次调用instance方法且同时检测到p是nullptr,则两个线程会同时构造一个实例给p,这将违反了单例的准则。
多线程下的分析:
正常情况下,如果线程A调用完了getInstance()之后,将m_instance初始化了,线程B再去调用getInstance()就不会在创建一个新的实例了,直接使用之前A创建好的实例;然而存在这一种情况,当线程A正在执行getInstance(),但是还没有创建好m_instance,此时B线程调用getInstance(),m_instance是nullptr,会new一个实例。A一个实例,B一个实例,这样就可能导致程序错误,同时,还会发生内存泄漏。