一句话来说就是,某个类只能有一个实例,提供一个全局的访问点。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
为什么需要单例模式
两个原因:
- 节省资源。一个类只有一个实例,不存在多份实例,节省资源。
- 方便控制。在一些操作公共资源的场景时,避免了多个对象引起的复杂操作。
应用场景:
1.数据库连接,一般整个程序里面只需要进行一次数据库连接即可,后续就不需要再关闭后连接了。
2.日志:只需要声明一个日志对象,任何时候程序只往一个日志内填写
单例模式包括哪些类型:
主要两大类:
1.懒汉式
2.饿汉式
二、单例的种类有哪些?之间有什么区别?
懒汉式:指全局的单例实例在第一次被使用时构建。
饿汉式:全局的单例实例在类装载(ClassLoader)时构建。(饿汉式单例性能优于懒汉式单例)
1、懒汉式与饿汉式区别:
1.1懒汉式默认不会实例化,外部什么时候调用什么时候new。饿汉式在类加载的时候就实例化,并且创建单例对象。
1.2、懒汉式是延时加载,在需要的时候才创建对象,而饿汉式是在虚拟机启动的时候就会创建。
1.3、懒汉式在多线程中是线程不安全的,而饿汉式是不存在多线程安全问题的。
懒汉式的线程安全问题:
线程安全的问题,当多线程获取单例时有可能引发竞态条件:第一个线程在if中判断 m_pInstance是空的,于是开始实例化单例;同时第2个线程也尝试获取单例,这个时候判断m_pInstance还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来; 解决办法:加锁
内存泄漏. 注意到类中只负责new出对象,却没有负责delete对象,因此只有构造函数被调用,析构函数却没有被调用;因此会导致内存泄漏。解决办法: 使用共享指针;
由于懒汉模式存在多线程的安全问题,因此会衍生出线程安全的懒汉式
其中一种方式就是使用智能指针和线程锁
另外一种方式是使用局部静态变量
这种方法又叫做 Meyers' SingletonMeyer's的单例, 是著名的写出《Effective C++》系列书籍的作者 Meyers 提出的。所用到的特性是在C++11标准中的Magic Static特性:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。
C++静态变量的生存期 是从声明到程序结束,这也是一种懒汉式。
这是最推荐的一种单例实现方式:通过局部静态变量的特性保证了线程安全 , 不需要使用共享指针,代码简洁;注意在使用的时候需要声明单例的引用 Single& 才能获取对象。
当然,C++11 后面出现std::call_once 就是解决这个问题,但是没有magic static 方便,所以推荐magic static的使用方法 。
引用:
2.5 万字详解:23 种设计模式 - 知乎 (zhihu.com)
【C++】C++ 单例模式总结(5种单例实现方法)_单例模式c++实现-CSDN博客
设计模式——单例模式(懒汉式与饿汉式)详解_懒汉式饿汉式是啥设计模式啊?-CSDN博客
C++ 单例模式-CSDN博客