第8章 创建型模式---单例模式
来源:互联网 发布:windows update失败 pe 编辑:程序博客网 时间:2024/06/05 10:44
一、单例模式
1、定义:保证一个类仅有一个实例,同时提供能对该实例加以访问的全局访问方法。
2、解决思路:
(1)、在类中,要构造一个实例,就必须调用类的构造函数。因此,为了在类的外部调用类的构造函数而创建实例,
需要将构造函数的访问权限设为protected或private.
(2)、需要提供全局访问点,就需要在类中定义一个static函数,返回在类中唯一构造的实例。
二、单例模式的实现
1、恶汉式,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。因为一开始就加载了,典型
的"空间换时间"。因为一开始就创建了实例,所以每次用时直接返回就好了。
//创建型模式:单例模式#include <iostream>using namespace std;//饿汉式:多线程安全class CSingleton{public://提供全局访问的访问点static CSingleton* getInstance() //静态成员变量,只能访问静态成员变量,可以间接访问成员变量和方法{static CSingleton instance; //静态局部对象,全局数据区内存中只有一份,只初始化第一次return &instance;}private://将构造函数设为私有属性,限制产生多个对象CSingleton(){};};int main(){CSingleton* s1 = CSingleton::getInstance();CSingleton* s2 = CSingleton::getInstance();printf("s1 = %p\n", s1);printf("s2 = %p\n", s2);cin.get();return 0;}
2、懒汉式:
(1)、典型的"时间换空间",即只会在全局访问方法中首次被调用时才被创建。
(2)、存在的问题:new出来的实例不能自动释放,可能存在内存泄漏问题。
解决方法:
a、方法一:单例类内部定义嵌套类(嵌套类的使用方法),并在单例类定义专门用于释放的静态成员,程序结束时析构
全局对象,会自动调用释放单例。
b、方法二:单例类中提供一个专门用于释放的静态成员函数,需要手动释放。
(3)、实际项目中:单例一般是全局对象,生命周期随软件运行结束而自然终止。但是如果单例中用到文件锁、文件
句柄或数据库连接,必须手动进行释放。
//创建型模式:单例模式#include <iostream>using namespace std;class CSingleton{private:static CSingleton* m_pinstance;//构造函数设为私有属性,限制产生多个对象CSingleton() {};//拷贝构造和=操作符设为私有,防止被复制CSingleton(const CSingleton& obj) {};CSingleton& operator=(const CSingleton& obj) {};public://提供全局访问点static CSingleton* getInstance(){if (m_pinstance == NULL){m_pinstance = new CSingleton();}return m_pinstance;}//资源释放:手工调用static void clearInstance(){if (m_pinstance != NULL){delete m_pinstance;m_pinstance = NULL;cout << "释放其他的资源..." << endl;}}};CSingleton* CSingleton::m_pinstance = NULL; //静态成员变量在类的外部添加作用于限定符进行初始化int main(){CSingleton* s1 = CSingleton::getInstance();CSingleton* s2 = CSingleton::getInstance();printf("s1 = %p\n", s1);printf("s2 = %p\n", s2);cin.get();CSingleton::clearInstance();while (1);return 0;}
//懒汉式2: 垃圾回收机制不一样,利用内部类自动回收
//创建型模式:单例模式#include <iostream>using namespace std;class CSingleton{private:static CSingleton* m_pinstance;//构造函数设为私有属性,限制产生多个对象CSingleton() {};//拷贝构造和=操作符设为私有,防止被复制CSingleton(const CSingleton& obj) {};CSingleton& operator=(const CSingleton& obj) {};class Gc{public:~Gc(){//在这里进行销毁,比如数据库连接、句柄等if (m_pinstance != NULL){delete m_pinstance;m_pinstance = NULL;cout << "释放其他资源..." << endl;}}};static Gc object; //声明public://提供全局访问点static CSingleton* getInstance(){if (m_pinstance == NULL){m_pinstance = new CSingleton();}return m_pinstance;}};//静态成员变量的初始化CSingleton* CSingleton::m_pinstance = NULL;//全局静态变量,自动销毁,实现对单例的垃圾回收CSingleton::Gc CSingleton::object; //定义,真正分配内存空间int main(){CSingleton* s1 = CSingleton::getInstance();CSingleton* s2 = CSingleton::getInstance(); //多个指针指向同一块内存空间,当释放某一个指针指向的资源时,地址值不变,但是内容是脏的,释放一次即可printf("s1 = %p\n", s1);delete s1;s1 = NULL;printf("s2 = %p\n", s2); //指针值不变,但是内容是脏的cin.get();return 0;}(4)、懒汉式:在高并发的情况下,注意单例模式的线程同步问题。
如果在一开始调用 getInstance()时,是由两个线程同时调用的(这种情况是很常见的),注意是同时,
(或者是一个线程进入 if 判断语句后但还没有实例化 Singleton 时,第二个线程到达,此时 singleton 还是为
null)这样的话,两个线程均会进入 getInstance(),而后由于是第一次调用getInstance(),所以存储在
Singleton 中的静态变量 singleton 为 null ,这样的话,就会让两个线程均通过 if 语句的条件判断,然后调
用new Singleton()了,
public static Singleton GetInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; }这样的话,问题就出来了,因为有两个线程,所以会创建两个实例,很显然,这便违法了单例模式的初衷了,那么如
何解决上面出现的这个问题(即多线程下使用单例模式时有可能会创建多个实例这一现象)呢?
双重检查锁定:
//多线程下的单例模式:处理懒汉式,因为饿汉式是线程安全的class CSingleton{public: //提供全局访问的访问点 static CSingleton* getInstance() { //双检查 if(m_pInstance == NULL) //第1次检查 { m_lock.lock(); if(m_pInstance == NULL) //第2次检查 { m_pInstance = new CSingleton; } m_lock.unlock(); } return m_pInstance; } private: //将构造函数设为私有属性 CSingleton(){}; static CSingleton* m_pInstance; static Lock m_lock; };三、单例模式的应用
1、单例模式的优点
(1)单例模式在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁的创建、销毁时,单例模式优势
非常明显
(2)由于单例模式只生成一个实例,所以减少了系统性能开销,当一个对象的产生需要较多资源时,可以启用一个单
例对象然后长驻内存又节约内存空间。
(3)单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源
文件的同时写操作。
(4)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映
射处理。
2、单例模式的缺点:
(1)单例模式一般没有接口,扩展很困难。因其“自动实例化”,而抽象类和接口是不能被实例化的。所以不能增加
接口
(2)单例模式对测试不利。在并行的开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也就不能虚
拟一个对象。
四、单例模式使用场景
(1)多线程之间共享一个资源或者操作同一个对象
(2)在整个程序空间中使用全局变量,共享资源
全局变量和单例模式相比的缺点:
全局变量就是一个对象的静态引用,全局变量确实可以提供单例模式实现的全局访问这个功能,
它并不能保证您的应用程序中只有一个实例,同时,在编码规范中,也明确指出,
应该要少用全局变量,因为过多的使用全局变量,会造成代码难读。
还有就是全局变量并不能实现继承(虽然单例模式在继承上也不能很好的处理,但是还是可以实现继承的)
单例模式:其在类中保存了它的唯一实例,这个类,它可以保证只能创建一个实例,
还提供了一个访问该唯一实例的全局访问点。
(3)在创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
(4)要求生成唯一序列号的环境或用单例做为一个计数器。
- 第8章 创建型模式---单例模式
- (第Ⅱ部分 创建型模式篇) 第1章 单件模式(Singleton Pattern)
- (第Ⅱ部分 创建型模式篇) 第1章 单件模式(Singleton Pattern)
- (第Ⅱ部分 创建型模式篇) 第1章 单件模式(Singleton Pattern)
- 创建型模式--单例模式(Singleton)
- 设计模式-创建型模式-单例
- 创建型模式-单例模式
- 创建型模式之单例模式
- 创建型设计模式-----单例模式
- 创建型模式--单例模式
- 创建型模式-单例模式
- 创建型模式:单例模式
- 创建型模式之单例模式
- 创建型模式 -- 单例模式
- 创建型模式之单例模式
- 创建型设计模式-单例模式
- 创建型模式-单例模式(singleton)
- 创建型模式之单例模式
- Qt 的 QToolTip使用一例
- 重构--提取规则和约束
- filter
- 加密算法的科普
- 极简写作语言-Markdown
- 第8章 创建型模式---单例模式
- Google Japan电面
- 【easyui】jQuery EasyUI Datagrid组件的完整的基础DOM结构
- 切面条
- 数据结构之顺序表2
- oj第九周训练ASCII码排序
- 2016.10.30 Bootstrap.4(组件开始)
- Leetcode #377 Combination Sum IV
- Ubuntu常用命令大全