一、单例模式
只能有一个类的对象,例如windows中的任务管理器,OS只需要维护这样一个对象即可,不需要每次都创建。使用单例模式来限制某个类创建实例对象的个数为1。
二、懒汉模式
在懒汉模式下,单例类的对象在需要的时候(调用 getInstance)才去创建
步骤如下:
1、构造函数私有化
由于不能在类外定义和 new,因此要将构造函数私有
2、类内提供私有的静态指针成员,并提供给外部创建对象的静态公有接口
由于构造函数私有了,那么提供的公有接口是没办法通过对象调用的,因此需要将该公有接口设为 static 的,又由于静态函数只能访问静态成员,因此类内私有的指针成员也需要设为 static 的,而为了防止在外部能访问到指针成员,造成可能的修改,因此需要将其设为私有属性,并在类外初始化为空
3、禁止构造、禁止拷贝
4、内存释放,嵌套一个回收类
不能提供接口供外部使用来释放,而是提供一个静态回收类,并在这个类的析构函数中释放掉单例类的指针成员,这样在程序结束时就会调用它的析构函数。
class SingletonLazy {
public:
static SingletonLazy* getInstance() {
if (_pSingle == nullptr)
_pSingle = new SingletonLazy;
return _pSingle;
}
struct Garbo {
~Garbo() {
if (_pSingle != nullptr)
delete _pSingle;
}
};
private:
SingletonLazy() { }
SingletonLazy(const SingletonLazy& single) { } //禁止拷贝
SingletonLazy& operator=(const SingletonLazy& single) { } // 禁止赋值
static SingletonLazy* _pSingle;
static Garbo _garbo;
};
SingletonLazy* SingletonLazy::_pSingle = nullptr;
void test1() {
SingletonLazy* p1 = SingletonLazy::getInstance();
SingletonLazy* p2 = SingletonLazy::getInstance();
cout << "p1 : " << (int)p1 << endl;
cout << "p2 : " << (int)p2 << endl;
if (p1 == p2) {
cout << "成功创建单例 SingletonLazy\n";
}
}
三、饿汉模式
饿汉模式在 main 函数之前就创建好了对象。
也就是将懒汉模式下的静态指针成员在类外初始化时就 new,这样一来 getInstance 方法中就不需要 new 了,直接返回即可
class SingletonHungry{
public:
static SingletonHungry* getInstance() {
return _pSingle;
}
private:
SingletonHungry() { }
SingletonHungry(const SingletonHungry& single) { } //禁止拷贝
SingletonHungry& operator=(const SingletonHungry& single) { } // 禁止赋值
static SingletonHungry* _pSingle;
};
SingletonHungry* SingletonHungry::_pSingle = new SingletonHungry;
void test2() {
SingletonHungry* p3 = SingletonHungry::getInstance();
SingletonHungry* p4 = SingletonHungry::getInstance();
cout << "p3 : " << (int)p3 << endl;
cout << "p4 : " << (int)p4 << endl;
if (p3 == p4) {
cout << "成功创建单例 SingletonHungry\n";
}
}
四、两种模式的线程安全性
在多线程的懒汉模式下,由于对象在需要时才调用 getInstance 创建,多个线程可能同时调用 getInstance, 结果创建了多个对象。
所以,懒汉模式是线程不安全的。而饿汉模式在 main 之前就创建好了,并在后面的 getInstance 调用中,返回的都是同一个对象,因此饿汉模式是线程安全的,在多线程情况下,通常都使用饿汉模式。
懒汉模式加锁实现线程安全
加锁,效率低一些。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
class SingletonLazy {
public:
static SingletonLazy* getInstance() {
{
lock_guard<mutex> lk(_mtx);
if (_pSingle == nullptr)
_pSingle = new SingletonLazy;
}
return _pSingle;
}
private:
SingletonLazy() { }
SingletonLazy(const SingletonLazy& single) { } //禁止拷贝
SingletonLazy& operator=(const SingletonLazy& single) { } // 禁止赋值
static SingletonLazy* _pSingle;
static mutex _mtx;
};
SingletonLazy* SingletonLazy::_pSingle = nullptr;