第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)要求生成唯一序列号的环境或用单例做为一个计数器。



0 0
原创粉丝点击