在讲单例模式之前,先说一下设计模式
设计模式
一些人将自己的编程经验,通过一些常见的问题或者常见的场景,给出一种解决方案或者一种套路,让后续的程序员在遇到相似问题时可以快速设计自己的代码,即一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结
常见设计模式
创建性模式:用于构建对象,将实现从系统当中分离处理
单例模式
结构性模式:用于许多不同对象之间形成大型的对象结构
适配器模式
行为型模式
观察者模式
单例模式
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理
特点
提供了一个唯一类的实例,具有全局变量的特点,在任何位置都可以通过单例类提供的获取类实例的方法来获取唯一实例
使用场景
数据池:用来缓存大量数据的数据结构,需要在不同的线程当中进行读取或者写入
内存池:为了程序的运行效率,将申请的内存自行管理,即当使用完成的时候不归还操作系统,自行管理
基础要点
全局只有一个实例
static 特性:禁止自己的构造、拷贝构造、复制拷贝
线程安全:用户通过接口获取实例,使用 static 修饰类的成员函数
饿汉模式
程序在初始化的时候进行实例化,资源在程序初始化的时候全部加载,不牵扯到线程安全
优缺点:运行流畅,但初始化耗时较长
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton(){};
// C++98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// C++11
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化
懒汉模式
资源在使用时才进行加载,即对象在使用的时候才实例化,存在线程不安全的风险
优缺点:运行没有那么流畅,存在线程安全问题,初始化较块
class Singleton
{
public:
static Singleton* GetInstance()
{
// 使用双检查的方式加锁,才能保证效率和线程安全
if (nullptr == m_pInstance) //提高效率
{
m_mtx.lock();
if (nullptr == m_pInstance) //保证线程安全
{
m_pInstance = new Singleton();
}
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;
void func(int n)
{
cout << Singleton::GetInstance() << endl;
}
// 多线程环境下演示上面GetInstance()加锁和不加锁的区别
int main()
{
thread t1(func, 10);
thread t2(func, 10);
t1.join();
t2.join();
cout << Singleton::GetInstance() << endl;
cout << Singleton::GetInstance() << endl;
}