单例模式(Singleton Pattern)
简述
单例模式是C++中常见的一种设计模式,它要求整个程序中只能存在一个实例,一般通过禁用构造和拷贝(这里的禁用指的是不对外暴露接口,而不是使用delete关键字删除),并提供唯一接口创建static实例来实现。根据实例对象的创建时机的不同,分为饿汉式和懒汉式单例模式:
饿汉式
代码实现
所谓饿汉式,指的是在程序启动时就创建对象实例,具体的实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Singleton{ private: static Singleton _instance; Singleton(){} ~Singleton(){} public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance(){ return _instance; } };
Singleton Singleton::_instance;
|
代码分析
饿汉模式天生是线程安全的,因为静态对象在启动时就会创建;但是如果对象太大,而程序有长时间未使用,可能会带来较大的资源消耗
懒汉式
代码实现
懒汉式有几种实现方式,这是因为最简单的实现版本具有线程安全问题,所以后续推出了几种优化方案,下面一一介绍一下:
初始版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Singleton{ private: static Singleton* _instance; Singleton(){} ~Singleton(){} public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton* getInstance(){ if(!_instance){ _instance = new Singleton(); } return _instance; } }; Singleton* Singleton::_instance = nullptr;
|
因为单例模式的对象实例和程序的生命周期相同,所以无需在析构函数中显式的delete掉new出来的对象
这里的实现是有线程安全问题的,如果两个线程同时进入Line 11的内存分配逻辑,会导致内存分配两个Singleton对象,而其中一个会被覆盖,导致内存泄露问题;
双重锁检查机制(DCL, Double-Check Lock)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <mutex>
class Singleton { private: Singleton() {} ~Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;
static Singleton* instance; static std::mutex mtx;
public: static Singleton* getInstance() { if (!instance) { std::lock_guard<std::mutex> lk(mtx); if (!instance) { instance = new Singleton(); } } return instance; } };
Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx;
|
第一重检查:性能优化,避免即使在无条件竞争的情况下依然需要申请锁,带来不必要的性能开销(锁竞争和上下文切换)
第二重检查:正确性保证,在锁保护的前提下再次确认未创建状态,避免多个线程重复执行new Singleton()
C++11局部静态变量(Meyers’ Singleton)
这是在C++11之后支持的线程安全版本的懒汉式的单例模式,实现更为简单:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Singleton{ private: Singleton(){} ~Singleton(){} public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance(){ static Singleton _instance; return _instance; } };
|