单例模式
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
懒汉式
在真正需要使用对象时才去创建该单例类对象。
实现
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。
我们使用的懒汉式写法也被称为:Double Check(双重校验) + Lock(加锁)
优点:
-
线程安全,加锁保证单例对象只被创建一次
-
性能高效,存在对象时减少锁的资源消耗
class FullSingle {
private:
int value;
static FullSingle* single;
static mutex mut;
private:
FullSingle():value(0) {}
FullSingle(const FullSingle&) = delete;
FullSingle& operator=(const FullSingle&) = delete;
public:
~FullSingle() {
delete single;
single = nullptr;
}
static FullSingle& getFullSingle() {
if (nullptr == single) {
mut.lock();
if (nullptr == single) {
single = new FullSingle;
}
mut.unlock();
}
return *single;
}
};
FullSingle* FullSingle::single = nullptr;
mutex FullSingle::mut;
方法剖析
-
构造函数置为private,保证单例对象不被外界创建。
-
删除拷贝构造,不能通过单例对象创建新对象
-
删除赋值重载函数,避免出现值语义的拷贝
饿汉式
在类加载时已经创建好该单例对象,等待被程序使用
实现
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
即是程序开始之前在数据区创建一个静态对象,保证该单例对象的唯一性。
优点:
不用使用额外资源(锁,指针);
生命周期更长,且避免失误销毁对象(析构可访问权限为private);
代码简单清晰
缺点:
-
如果存在多个单例对象且这几个单例对象相互依赖,可能会出现程序崩溃的危险。原因:对编译器来说,静态成员变量的初始化顺序和析构顺序是一个未定义的行为;具体分析在懒汉模式中也讲到了。
-
在程序开始时,就创建类的实例,如果Singleton对象产生很昂贵,而本身有很少使用,这种方式单从资源利用效率的角度来讲,比懒汉式单例类稍差些。但从反应时间角度来讲,则比懒汉式单例类稍好些。
class HungerSingle {
private:
int value;
static HungerSingle a;
private:
HungerSingle() :value(0) {}
HungerSingle(const HungerSingle&) = delete;
HungerSingle& operator=(const HungerSingle&) = delete;
~HungerSingle() {}
public:
static HungerSingle& getHungerSingle() {
return a;
}
};
HungerSingle HungerSingle::a;
方法剖析
-
构造函数置为private,保证单例对象不被外界创建。
-
删除拷贝构造,不能通过单例对象创建新对象
-
删除赋值重载函数,避免出现值语义的拷贝
-
析构方法置为private,因为其生命周期为程序生命周期,在程序结束时被系统释放。