首页 > 代码库 > 单例模式
单例模式
1.什么是单例模式?
单例模式也称为单件模式、单子模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类,即设计的一个类成为单例。通过单例模式可以保证系统中一个这个类只有一个实例。即一个类只有一个对象实例。(设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结)。单例模式是设计模式中最简单的形式之一。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
2.设计单例模式要点
目标功能:一是设计某个类且只能有一个实例;二是这个类必须自行创建这个实例;三是类必须自行向整个系统提供这个实例。保证全局只有一个唯一实例对象;提供获取这个唯一实例的接口。
具体实现要点:一是单例模式的类只提供私有的构造函数;二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象(只能在类里创建对象)。
单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法。唯一的实例是类的一个普通对象,但设计这个类时,让它只能创建一个实例并提供对此实例的全局访问。唯一实例类Singleton在静态成员函数中隐藏创建实例的操作。习惯上把这个成员函数叫做GetInstance(),它的返回值是唯一实例的指针。
3.优缺点
优点:
4.两种实现方式
(1)懒汉模式 ---lazy load + 相对而言 复杂--各种场景下都适用
1 //设计一个类,成为单例 2 class Lock //自造轮子 3 { 4 public: 5 //RAII思想 6 Lock(mutex& mx) 7 :_mx(mx) 8 { 9 _mx.lock(); 10 } 11 ~Lock() 12 { 13 _mx.unlock(); 14 } 15 private: 16 Lock(const Lock&); 17 Lock& operator=(const Lock&); 18 19 mutex& _mx; 20 }; 21 //懒汉模式 22 class Singleton 23 { 24 public: 25 //1.获取本身静态私有对象的静态成员函数 26 static Singleton* GetInstance() 27 { 28 if (_inst == NULL) { 29 //加锁(防死锁)-->线程安全 30 //lock_guard<mutex> lock(_mtx); 31 Lock lock(_mtx); 32 33 Singleton* tmp = new Singleton; 34 35 //双检查 36 MemoryBarrier(); 37 _inst = tmp; 38 } 39 return _inst; 40 } 41 42 static void DelInstance() 43 { 44 lock_guard<mutex> lock(_mtx); 45 if (_inst) 46 { 47 cout << "delete" << endl; 48 delete _inst; 49 _inst = NULL; 50 51 } 52 } 53 struct GC 54 { 55 ~GC() 56 { 57 DelInstance(); 58 } 59 }; 60 61 void Print() 62 { 63 cout << "Singleton:" << _a << endl; 64 } 65 private: 66 //2.构造函数定义为私有 67 Singleton() 68 :_a(0) 69 {} 70 ~Singleton() 71 {} 72 73 //防拷贝 74 Singleton(const Singleton&); 75 Singleton& operator=(const Singleton&); 76 77 int _a; 78 79 //3.静态私有对象 80 static Singleton* _inst; 81 82 static mutex _mtx; 83 }; 84 Singleton* Singleton::_inst = NULL; 85 mutex Singleton::_mtx; 86 static Singleton::GC gc; 87 88 void Test() 89 { 90 Singleton::GetInstance()->Print(); 91 Singleton::GetInstance()->Print(); 92 Singleton::GetInstance()->Print(); 93 Singleton::GetInstance()->Print(); 94 Singleton::GetInstance()->Print(); 95 }
用户访问唯一实例的方法只有GetInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。GetInstance()使用懒惰初始化,也就是说它的返回值是当这个函数首次被访问时被创建的。这是一种防弹设计——所有GetInstance()之后的调用都返回相同实例的指针:
Singleton* p1 = Singleton :: GetInstance();
Singleton* p2 = p1->GetInstance();
Singleton & ref = * Singleton :: GetInstance();
对GetInstance稍加修改,这个设计模板便可以适用于可变多实例情况。
关于Singleton(const Singleton);和 Singleton & operate = (const Singleton&);函数,需要声明成私有的,并且只声明不实现。这样,如果用上面的方式来使用单例时,不管是在友元类中还是其他的,编译器都是报错。考虑到线程安全、异常安全用了锁。
(2)饿汉模式 ----一开始(main) load 简洁 适用性会受到限制 动态库
1 //饿汉模式 2 class Singleton2 3 { 4 public: 5 //1.获取本身静态私有对象的静态成员函数 6 static Singleton2 GetInstance() 7 { 8 assert(_inst); 9 return *_inst; 10 } 11 12 void Print() 13 { 14 cout << "Singleton:" << _a << endl; 15 } 16 17 private: 18 //2.构造函数定义为私有 19 Singleton2() 20 :_a(0) 21 {} 22 23 //防拷贝 24 Singleton2(const Singleton2&); 25 Singleton2& operator=(const Singleton2&); 26 27 int _a; 28 //3.静态私有对象 29 static Singleton2* _inst; 30 31 }; 32 void Test() 33 { 34 Singleton::GetInstance().Print(); 35 Singleton::GetInstance().Print(); 36 Singleton::GetInstance().Print(); 37 }
这是一种较简单的实现方式,但问题也较多,应用用不太广泛。第一种方法可适用于各种复杂情况下。
4.单例的应用广泛
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
单例模式