单例模式可以说是老生常谈的面试考点。从最经典的单例模式实现开始,我们逐步提供了多种单例模式的实现方式,并一一分析它们的优缺点。
方案一:教科书经典方案
使用一个静态指针管理对象, 当第一次需要时创建此对象。
// 在此略去构造函数等无关代码,仅讨论单例模式本身
class Singleton{
public:
static Singleton& getInstance(){
if(instance_ == nullptr)
instance_ = new Singleton{
};
return *instance_;
}
private:
static Singleton* instance_;
};
显然,这种经典的实现方式存在诸多问题:
- 仅能单线程下正确运行,多线程时不保证对象唯一。
- 需要手动释放申请的资源,以防内存泄漏。
- 每次调用都存在一次判断的开销(几乎无法避免)。
首先,针对多线程正确申请资源这一点,可以使用DCL(Double check lock),即使用一个mutex
保护资源的申请过程,并且通过两次判断避免争抢。这就引入了第二个方案。
方案二:DCL
双重锁定检查(DCL)在两次判断中进行一个加锁操作,首先第一次判断,确定了此时对象还未构造,故申请互斥锁并进入临界区完成对象构造。而如果发生争抢,也会被互斥量阻拦,随着构造完成,第二次检查时将发现对象已存在,保证了线程安全。
class Singleton {
public:
static Singl