单例模式作为最经典的设计模式,知识点很多,而且面试常考,值的新手深度学习一下。
根据各类帖子和chatGPT,我整理出了一版比较经典的单例模式,可以供面试手撕或学习之用。
众所周知:
饿汉很饥渴,直接创建单例,比较简单。
懒汉很懒,直到第一次向他索要单例,他才开始创建这个单例。
因此,再多线程中:
饿汉很安全,单例在早已在静态全局区等待调用了。
懒汉很不安全,如果多个线程同时索要单例,单例创建方法可能被多个线程同时调用,导致多个单例的同时创建。
这又引申出一个问题,饿汉&懒汉中,都有get_instance()这个方法,但是其功能天壤之别:
饿汉中,该方法仅用来调用早已创建好的静态全局变量 instance 。
懒汉中,该方法要在第一次调用时创建instance,在之后的调用时才是直接return instance。
在此着重实现一下懒汉模式
#include <memory>
#include <mutex>
using namespace std;
class LazySingleton {
public:
static shared_ptr<LazySingleton> get_instance();
private:
static shared_ptr<LazySingleton> instance;
static mutex m_mutex;
LazySingleton() = default;
~LazySingleton() = default;
LazySingleton(const LazySingleton& other) = delete;
LazySingleton& operator=(const LazySingleton& other) = delete;
};
shared_ptr<LazySingleton> LazySingleton::instance = nullptr;
mutex LazySingleton::m_mutex;
shared_ptr<LazySingleton> LazySingleton::get_instance() {
if (instance == nullptr) {
lock_guard<mutex> lock(m_mutex);
if (instance == nullptr) {
instance = make_shared<LazySingleton>();
}
}
return instance;
}
注意点:
1.禁止拷贝语意
2.双重锁:一层确认instance还没被创立;二层避免竞态
3.静态成员是属于全局的,一般类内只做声明,类外实现
4.采用lock_guard而不是直接使用m_mutex.lock() ,是避免锁内部异常,导致无法解锁,甚至死锁。(此例可以不用)