摘要:今天看面试会问到单例模式,今天我们就来学习一下。
介绍
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类的频繁地创建与销毁。
使用场景:当想控制实例数目,节省资源的时候。
关键代码:构造函数是私有的;拷贝构造函数是私有的;局部变量是静态的。
实现
不支持并发
/* * method-1 * 延迟初始化,只有在第一次调用 getInstance 的时候才会初始化实例 * 存在问题:线程不安全版本,可能有多个线程在 if 条件处产生竞态条件,从而产生多个实例 * */ class Singleton { public: static Singleton* getInstance() { //先检查对象是否存在 if(m_instance == nullptr) { m_instance = new Singleton(); } return m_instance; } private: //构造函数私有化 Singleton(); //析构函数私有化 ~Singleton(); //拷贝构造函数私有化 Singleton(const Singleton& other); //赋值函数私有化 Singleton& operator = (const Singleton &); //静态局部变量 static Singleton* m_instance; }; //静态成员需要在类外赋值 Singleton* Singleton::m_instance = nullptr;
支持并发[加锁]
/* * method-2 * 线程安全版本 * 存在问题:但锁的代价过高 * */ class Singleton { public: static Singleton* getInstance() { Lock lock; //加锁,此处为伪代码 if(m_instance == nullptr) { m_instance = new Singleton(); } unlock; return m_instance; } private: Singleton(); ~Singleton(); Singleton(const Singleton& other); Singleton& operator = (const Singleton &); static Singleton* m_instance; }; //静态成员需要在类外赋值 Singleton* Singleton::m_instance = nullptr;
双检查锁
/* * method-3 * 线程安全版本,双检查锁 * 存在问题:产生内存读写乱序问题 * 分析:实际上 m_instance = new Singleton() 这条语句是分三个步骤来执行的 * (1) 分配了一个 Sington 类型对象所需要的内存 * (2) 再分配的内存出构造 Singleton 类型的对象 * (3) 把分配的内存地址赋给指针 m_instance * 编译器会给我们做些优化,其实这三个步骤不一定是顺序执行的,只能确定步骤(1) 是最先执行的,步骤(2)(3) 却不一定。 * 如果线程 A 在调用执行 m_instance = new Singleton() 的时候是按照 (1)(3)(2)的顺序执行的,那么刚刚执行完(3)给 m_instance 分配了内存 * 此时 m_instance 就不再是 nullptr * 如果此时切换到了线程B, 由于 m_instance != nullptr, 所以线程B会直接执行 return m_instance 得到一个对象,而这个对象并没有被真正构造 * 解决方式:在声明 m_instance 变量的时候,要加上 volatile 关键字修饰 * */ class Singleton { public: Singleton* getInstance() { if(m_instance == nullptr) { Lock lock; if(m_instance == nullptr) { m_instance = new Singleton(); } unlock; } } private: Singleton(); ~Singleton(); Singleton(const Singleton& other); Singleton& operator = (const Singleton &); static Singleton* m_instance; }; //静态成员需要在类外赋值 Singleton* Singleton::m_instance = nullptr;
C++11 极简实现
/* * method-4 * 最为简单的实现方式 * 存在问题:C++11及以后的版本下才正确,之前的版本不能这么写,原因是 C++11 在底层可以保证 static 变量是多线程安全的 * */ class Singleton { public: Singleton* getInstance() { static Singleton instance; return &instance; } private: Singleton(); ~Singleton(); Singleton(const Singleton& other); Singleton& operator = (const Singleton &); };