单例模式
概念:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所以程序模块共享
单例模式下分为饿汉模式和懒汉模式两种
饿汉式
无论将来用不用这个对象,程序启动时就创建一个唯一的实例对象
场景:适合与创建对象消耗资源少且使用活跃的情况(对性能要求高)
特性:程序启动慢,如果存在多个单例对象的创建,创建的顺序将不确定
创建过程:
- 声明一个静态对象指针
- 在程序入口之前就完成对单例模式的初始化
- 私有化构造函数(拷贝构造、赋值运算符重载)
- 要注意使用过后对资源的回收
class Singleton {
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton(){};
// C++98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// or
// C++11
//Singleton(Singleton const&) = delete;
//Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
// 在程序入口之前就完成单例对象的初始化
Singleton Singleton::m_instance;
懒汉式:
当需要用到对象的时候再进行创建(延迟加载)
场景:适用于构造对象需要占用大量资源或者非常耗时的情况
特性:进程启动无负载,多个单例对象的创建顺序可以自由控制
构建方式:
- 私有化一个静态对象指针
- 有一个获取这个对象的唯一公有方法来构造对象
- 私有化构造(拷贝构造、赋值运算符重载)
- 内嵌一个垃圾回收类(当该对象析构的时候自动销毁该静态指针)
要注意在创建对象的时候一定要进行双重检查对象已经创建
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton {
public:
static Singleton* GetInstance() {
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
//第一处check为了效率
if (nullptr == m_pInstance) {
m_mtx.lock();
//第二处check为了安全
if (nullptr == m_pInstance) {
try(){
m_pInstance = new Singleton();
if(m_pInstance == nullptr)
throw "instance fail";
}
catch(...)
{
//异常处理应当解锁,否则将会形成死锁的情况
m_mtx.unlock();
cout << "new fail" << endl;
return NULL;
}
}
m_mtx.unlock();
}
return m_pInstance;
}
// 实现一个内嵌垃圾回收类
class CGarbo {
public:
~CGarbo()
{
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
private:
// 构造函数私有
Singleton(){};
// 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// 单例对象指针
static Singleton* m_pInstance;
//互斥锁
static mutex m_mtx;
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
//这里的创建对象的方式还有以下两种形式
//方式二:范围锁,如果new出现异常不会导致死锁
static CSingleton* GetInstance()
{
if (Instance == nullptr)
{
lock_guard<mutex> lc(lock);
Instance = new CSingleton();//在范围锁这里不用异常处理,因为不会导致死锁
}
return Instance;
}
//方法三:内存栅栏
static CSingleton* GetInstance()
{
if (Instance == nullptr)
{
lock_guard<mutex> lc(mutex);
CSingleton* temp = new CSingleton();
//内存栅栏,new分为三步
//1,开辟空间,2,调用构造函数,3,赋值给instance
MemoryBarrier();
//但是有些编译器会出现优化,先赋值
//在调用构造函数,可能会出错误,然后就用MemoryBarrier();
//如果失败了和instance也没有关系,
Instance = temp;
}
return Instance;
}