一、Singleton 单例模式
定义:
单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性。
使用场景:
在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动
程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用
的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
特点:
- 单例类只有一个实例对象;
- 构造函数和析构函数为私有类型,即该单例对象必须由单例类自行创建;
- 拷贝构造函数和赋值构造函数是私有类型,保证实例的唯一性;
- 单例类对外提供一个访问该单例的全局访问点,一个静态方法。
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 保证程序的线程安全,可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背
开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
1. 懒汉式-线程不安全
线程安全问题仅出现在第一次初始化(new)过程中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Singleton { private: static Singleton* instance;
Singleton() {}; ~Singleton() {}; Singleton(const Singleton&); const Singleton& operator=(const Singleton&);
public: static Singleton* getInstance() { if (instance == NULL) instance = new Singleton();
return instance; } };
Singleton* Singleton::instance = nullptr; Singleton* instance_1 = Singleton::getInstance(); Singleton* instamce_2 = Singleton::getInstance();
|
- 获取了两次实例,但是只有一个构造函数被调用,即只生成了唯一实例。
- 可能存在的问题:线程安全:实例化竞争;内存泄漏:只有构造没有析构
2. 加锁的懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class SingleInstance_1 { public: static SingleInstance_1*& getInstance(); static void deleteInstance();
private: SingleInstance_1(); ~SingleInstance_1();
SingleInstance_1(const SingleInstance_1& signal) = delete; const SingleInstance_1& operator=(const SingleInstance_1& signal) = delete;
static SingleInstance_1* m_Instance_1; static std::mutex mutex; };
|
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 27 28 29
| SingleInstance_1* SingleInstance_1::m_Instance_1 = nullptr; std::mutex SingleInstance_1::mutex;
SingleInstance_1*& SingleInstance_1::getInstance() { if (SingleInstance_1::m_Instance_1 == nullptr) { std::unique_lock<std::mutex> lock(SingleInstance_1::mutex); if (SingleInstance_1::m_Instance_1 == nullptr) { SingleInstance_1::m_Instance_1 = new (std::nothrow) SingleInstance_1(); } }
return m_Instance_1; }
void SingleInstance_1::deleteInstance() { std::unique_lock<std::mutex> lock(SingleInstance_1::mutex); if (SingleInstance_1::m_Instance_1) { delete m_Instance_1; m_Instance_1 = nullptr; } }
SingleInstance_1::SingleInstance_1() { std::cout << "create\n "; }; SingleInstance_1::~SingleInstance_1() { std::cout << "delete\n"; };
|
3. 加锁的懒汉式 - 智能指针
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 27 28 29 30 31 32 33 34 35
| class SingleInstance_2 { public: static std::shared_ptr<SingleInstance_2> getInstance(); ~SingleInstance_2() { std::cout << this << std::endl; }
private: SingleInstance_2() { std::cout << this << std::endl; }
SingleInstance_2(const SingleInstance_2& single) = delete; const SingleInstance_2& operator=(const SingleInstance_2& single) = delete; };
static std::shared_ptr<SingleInstance_2> m_Instance = nullptr; static std::mutex mutex;
std::shared_ptr<SingleInstance_2> SingleInstance_2::getInstance() { if (m_Instance == nullptr) { std::unique_lock<std::mutex> lock(mutex); if (m_Instance == nullptr) { auto temp = std::shared_ptr<SingleInstance_2>(new SingleInstance_2()); m_Instance = temp; } } return m_Instance; }
|
4. 推荐的懒汉式 - 静态局部变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class SingleInstance_3 { public: ~SingleInstance_3() { std::cout << this << std::endl; }; SingleInstance_3(const SingleInstance_3&) = delete; const SingleInstance_3& operator=(const SingleInstance_3&) = delete;
static SingleInstance_3& getInstance() { static SingleInstance_3 m_Instance; return m_Instance; } private: SingleInstance_3() { std::cout << this << std::endl; }; };
|
- 通过局部静态变量的特性保证了线程安全
- 不需要使用共享指针,代码简洁
- 如果采用返回指针实现而不是返回引用实现,无法避免用户使用delete instance导致对象被提前销毁
5. 饿汉式
相比于懒汉式而言,代码一运行就初始化创建实例 ,本身就线程安全
1 2 3
|
Singeleton* Singeleton::m_Instance = new (std::nothrow) Singleton();
|
std::nothrow
普通new一个异常的类型std::bad_alloc,机在内存分配失败的时候直接报异常。在内存不足时,new (std::nothrow) 并不抛出异常,而是将指针置NULL,因此 std::nothrow 可以实现对非零指针的检查。