单例模式
一个类只能创建一个对象即为单例模式,该模式中保证系统中该类只有一个实例并提供一个访问他的全局访问点,该实例被所有程序模块共享。
应用场景:
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置文件【由一个单例对象统一读取,然后其他对象从该单例对象获取配置信息。
单例模式的两种实现模式:饿汉和懒汉
饿汉模式
在程序启动时就创建一个唯一的单例对象。
因为一个类中创建一个实例对象,所以我们需要控制该类对象的创建,最基本的方式就是将构造函数私有化,应为只能有一个,所以也不能被拷贝,和赋值 被其他对象获取
- 构造函数私有化
限制对象的创建
- 拷贝构造私有化
- 赋值运算符重载私有化
防止通过拷贝和赋值重新生成其他的实例化对象
实例:
6 //饿汉模式:在程序开始之前就创建一个实例化对象
7
8 class SingletonE{
9 public:
10 static SingletonE* GetInstance()//将其声明为一个静态,以便不用创建对
象也可以访问
11 {
12 return &_Instance;
13 }
14 private:
15 //构造私有化
16 SingletonE()
17 {
18 cout<<"Singletong:饿汉模式"<<endl;
19 };
20 //拷贝构造私有化
21 SingletonE(const SingletonE& p)
22 {};
23 SingletonE operator=(const SingletonE& p)
24 {};//无需定义,仅声明即可,可使用c++11特性将其声明成已删除函数,防止通过友元访问,断绝所有访问路径
25
26 static SingletonE _Instance;//在类创建时就已将创建了一个静态的成员>
变量,只需要在需要时直接调用即可
27 };
28
29 SingletonE SingletonE::_Instance;
饿汉的优点在于入如果在多线程高并发的环境下频繁使用可以降低资源的竞争,提高响应速度
但是如果在程序开始时要加载插件,比较大的配置文件,初始化网络链接,读取文件的一方面比较费时,而且还有可能用不到,此时就提出了懒汉(延迟加载)模式
懒汉模式
在使用时才创建实例化对象
不会在一开时就创建实例化对象而是等到要使用时才进行创建。
懒汉的基础结构与饿汉相同,都要做到防拷贝和构造私有。仅在程序开始时创建一个对象的空指针,在需要时创建对象,使其不为空即可。
但考虑到线程安全的问题引入了锁。
饿汉不存在线程安全的问题,因为只有一个对象,每个线程都只能看到唯一的一个实例对象。
29 SingletonE SingletonE::_Instance;
30
31 class SingletonL{
32 public:
33 static SingletonL* GetpInstance()
34 {
35 if(_pInstance==NULL) //双检查,防止其他进程多次加锁,解锁降低程序
的效率 【2】
36 {
37
38 m_mtx.lock();//加锁 保护线程安全,防止两线程并行时创建两个实例>
化对象 【1】
39 if(_pInstance==NULL) //判断是否是第一次创建该对象
40 {
41 _pInstance=new SingletonL;
42 teturn _pInstance;
43 }
44 m_mtx.unlock();//解锁
45 }
W> 46 }
47
48
49
50 //内嵌一个垃圾回收类在程序结束后自动释放对象
51 class CGarbo{
52 public:
53 ~CGarbo(){
54 if(_pInstance!=NULL)
55 delete _pInstance; //内部类是外部类的友元,可以访问其私有数据
56 }
57 private:
58 };
59
60 static CGarbo Garbo; //定义一个全局的静态变量,在程序结束时,系统会>
自动调用他的析构函数,从而会将实例化对象释放
61 private:
62 SingletonL()
63 {
64 cout<<"懒汉模式"<<endl;
65 }; //防拷贝
66 SingletonL(const SingletonL& pt)=delete;
67 SingletonL operator= (const SingletonL& p)=delete;
68 static SingletonL* _pInstance;
69 static mutex m_mtx;//定义一个互斥锁
70 };
71
72 SingletonL* SingletonL::_pInstance=NULL;
73 mutex SingletonL::m_mtx;
74 SingletonL::CGarbo Garbo;
-
【1】 如果在需要创建实例对象时,两个线程并行走到此处,都发现该指针为空,就会创建两个实例化对象,此时引入锁,每次只能进入一个,当第二个线程再次进入时发现该指针已不为空,就无法再创建对象,解决了线程安全的问题。
-
【2】如果存在大量线程,每次都要进行加锁解锁的操作,大大降低了程序的效率,所以进行双检查,避免无意义的锁操作。