要说单例模式我们先来说设计模式
设计模式
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式是软件开发人员在软件开
发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是针对设计问题的通用解决方案。
使用设计模式的目的
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化
是软件工程的基石脉络,如同大厦的结构一样
单例模式
概念
一个类只能创建一个对象, 即单例模式, 单例模式可以保证系统中一个类只有一个实例, 并提供一个全局的访问点, 这个实例
被所有程序模块共享
应用实例
1. 线程池
2. 一些设备管理器, 比如打印机, 我们要控制多个打印机打印不同的内容
特点
1. 一个类只能有一个实例
2. 单例的类必须创建自己的唯一实例
3. 单例的类必须让其它所有对象可以访问到这个实例
实现
饿汉
1 .不管后面能不能用到, 程序启动之前将创建一个唯一的实例. 因此启动慢, 运行后速度快, 流畅
饿汉单例模式是线程安全的
2. 不能控制多个单例的初始化顺序
class Singleton { public: //提供一个静态方法, 不能是普通的, 普通的方法需要对象来调用 // 但是对象还没有创建出来 static Singleton* getIns() { //不能返回值, 会发生一次拷贝构造, 要直接返回当前创建的对象 return &_Singleton; } private: ///构造函数私有化, 不能任意创建对象 Singleton(){} //声明成静态成员, 启动时会已经创建好, 不存在于对象模型中, 是全局的 static Singleton _Singleton; //拷贝构造私有, 防拷贝 Singleton(const Singleton&) = delete; }; Singleton Singleton::_Singleton;
懒汉
如果单例对象构造十分耗时或者占用很多资源,比如加载插件,初始化网络连接等等,而有可能该对象程序运行时不会
用到,但是导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式更好,使用时再创建全局唯一的对象
启动快, 可以控制多个单例的实例化顺序
1. 非线程安全的单例模式
class Singleton { public: static Singleton* getIns() { //先判断有没有创建, 没有则创建 if (_SL == nullptr) { _SL = new Singleton; } return _SL; } private: Singleton(){} Singleton(const Singleton&) = delete; //定义一个指针, 程序启动时我们可以先给一个nullptr static Singleton* _SL; }; Singleton* Singleton::_SL = nullptr;
这样写有一个非常大的问题就是线程安全, 多个线程进行操作的时候, 是一个并行的过程, 同时对临界资源进行争抢操作的
时候,就有可能会出现线程安全的问题.
"如果一个线程走到 _SL = new Singleton, 这个时候准备赋值之前又有一个线程判断_SL是空的, 也会进入if语句, 也会执
行_SL = new Singleton, 这个时候new Singleton执行了两次就会调用两次构造函数, 创建出来两个对象, 但是_SL只能指
向一个对象, 这个时候_SL就会指向最后一个线程创建的对象, 前面创建出来的对象相当于都丢了, 就会造成内存泄漏. 所有我
们一定要考虑线程安全的问题, 一次只要能有一个线程进行操作."
2. 线程安全的单例模式
class Singleton { public: static Singleton* getIns() { //先判断有没有创建, 没有则创建 //当多批线程运行时, 如果只有一重锁, 那么就会导致每个线程进来直接加锁, 效率非常低 //所以我们要使用双重判断, 保证执行效率和线程安全 if (_SL == nullptr) { _mutex.lock(); if (_SL == nullptr) { _SL = new Singleton; } _mutex.unlock(); } return _SL; } private: Singleton(){} Singleton(const Singleton&) = delete; //定义一个指针, 程序启动时我们可以先给一个nullptr static Singleton* _SL; static mutex _mutex; }; Singleton* Singleton::_SL = nullptr; mutex Singleton::_mutex;
一般我们都使用双检查的方式, 外部保证效率, 内部保证线程安全.
区别
饿汉 :
1. 程序启动之前直接实例化一个单例的对象, 启动慢, 但是运行起来之后比较快, 不能控制单例的实例化顺序
2. 线程安全
懒汉 :
1. 等到使用的时候再实例化对象, 启动快, 可以控制多个单例的实例化顺序
2. 非线程安全, 实现比较复杂
以上就是关于单例模式的一些问题, 谢谢观看