zoukankan      html  css  js  c++  java
  • 第21章单例模式

    一 单例模式

    • 单例模式,保障一个类仅有一个实例,并提供一个访问他的全局访问点
    • 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法是,让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

    二 注意:

    • 使用单例模式,只能保证一个线程内对象不会被多次创建,而不能保证多线程的情况。因此,需要考虑多线程的话,就要用锁。

    三 C++代码实现
    在不考虑多线程时,单例模式实现的C++代码

    // lesson2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include "pch.h"
    #include <iostream>
    using namespace std;
    
    
    
    class Singleton
    {
    private:
    	static Singleton* instance;
    private:
    	Singleton()  //构造方法让其private,这就堵死了外界利用new创建此类实例的可能
    	{
    
    	}
    public:
    	static Singleton* GetInstance()  //此方法是获得本类实例的唯一全局访问节点
    	{
    		if (instance == NULL)
    		{
    			instance = new Singleton();
    		}
    		return instance;
    	}
    };
    
    Singleton* Singleton::instance = NULL;
    
    int main()
    {
    	Singleton* s1 = Singleton::GetInstance();
    	Singleton* s2 = Singleton::GetInstance();
    	if (s1 == s2)  //比较两次实例化后对象的结果是实例相同
    	{
    		cout << "两个对象是相同的实例" << endl;
    	}
    	return 0;
    }
    
    

    上述代码:把类的构造方法改为私有,所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效。这样对于外部代码,不能用new来实例化它,但是我们完全可以再写一个public方法,叫做GetInstance(),这个方法的目的就是返回一个实例,而在此方法中,去做是否有实例化的判断,如果没有实例化过,由调用private的构造方法new出这个实例,它可以调用是因为它们在同一个类中,private方法可以被调用。
    多线程时的单例,使用双重锁定

    #include "pch.h"
    #include <iostream>
    #include <WinSock2.h>
    using namespace std;
    
    
    
    class Singleton
    {
    private:
    	static Singleton* instance;
    	//临界区,防止多线程产生多个实例
    	static CRITICAL_SECTION m_Sec;
    private:
    	Singleton()  //构造方法让其private,这就堵死了外界利用new创建此类实例的可能
    	{
    
    	}
    public:
    	static CRITICAL_SECTION* getlock()
    	{
    		return &m_Sec;
    	}
    	static Singleton* GetInstance()  //此方法是获得本类实例的唯一全局访问节点
    	{
    		//双重锁定
    		if (NULL == instance)
    		{
    			EnterCriticalSection(&m_Sec);
    			if (instance == NULL)
    			{
    				instance = new Singleton();
    			}
    			LeaveCriticalSection(&m_Sec);
    		}
    		return instance;
    	}
    };
    
    Singleton* Singleton::instance = NULL;
    CRITICAL_SECTION Singleton::m_Sec = CRITICAL_SECTION();
    
    int main()
    {
    	//初始化临界区
    	InitializeCriticalSection(Singleton::getlock());
    	
    	Singleton* s1 = Singleton::GetInstance();
    	Singleton* s2 = Singleton::GetInstance();
    
    	//删除临界区
    	DeleteCriticalSection(Singleton::getlock());
    
    	if (s1 == s2)  //比较两次实例化后对象的结果是实例相同
    	{
    		cout << "两个对象是相同的实例" << endl;
    	}
    	return 0;
    }
    

    使用双重锁的原因:
    当instance为NULL并且同时有两个线程调用GetInstance()方法时,他们都将通过第一层的if(NULL == instance)的判断,然后由于lock机制,这两个线程则只有一个进入,另一个在外排队等候,必须要其中一个进入并出来后,另一个才能进入。而此时如果没有了第二重的instance是否为null的判断,则第一个线程创建了实例,而第二个线程还可以继续再创建新的实例,就达不到单例的目的了。

    四 以下是一些拓展

    • 缓存与单例
    //本例回避多线程的安全问题
    #include "pch.h"
    #include <iostream>
    #include <map>
    #include <string>
    using namespace std;
    
    class Singleton;
    static map<string, Singleton*> myMap = map<string, Singleton*>();
    
    //懒汉 延时加载
    class Singleton
    {
    private:
    	Singleton()
    	{
    		m_singer = NULL;
    		cout << "单例正在构建" << endl;
    	}
    public:
    	static Singleton* GetInstance()
    	{
    		if (myMap.find(DEFAULT_KEY) != myMap.end())
    		{
    			return myMap.find(DEFAULT_KEY)->second;
    		}
    		if (m_singer == NULL)
    		{
    			m_singer = new Singleton;
    			myMap[DEFAULT_KEY] = m_singer;
    		}
    		return m_singer;
    	}
    private:
    	static Singleton *m_singer;
    	static string DEFAULT_KEY;
    };
    
    Singleton* Singleton::m_singer = NULL;
    string Singleton::DEFAULT_KEY = "ONE";
    
    int main()
    {
    	Singleton *p1 = Singleton::GetInstance();
    	Singleton *p2 = Singleton::GetInstance();
    	cout << hex << p1 << ' ' << hex << p2 << endl;
    	system("pause");
    	return 0;
    }
    
    • 多例拓展
    #include "pch.h"
    #include <iostream>
    #include <map>
    #include <string>
    using namespace std;
    //单例模式和map配合,构成缓存的形式
    //缓存的实例个数
    const static int NUM_MAX = 5;
    
    class Singleton;
    static map<int, Singleton*> myMap = map<int, Singleton*>();  //int用来控制数列
    
    class Singleton
    {
    private:
    	Singleton()
    	{
    		m_singer = NULL;
    		cout << "正在构建Singleton" << endl;
    	}
    public:
    	static Singleton* GetInstance()
    	{
    		m_singer = myMap[m_InstanceCount];
    	
    		if (m_singer == NULL)
    		{
    			m_singer = new Singleton;
    			myMap[m_InstanceCount] = m_singer;
    		}
    		if (m_InstanceCount > NUM_MAX) 
    		{
    			m_InstanceCount = 1;
    		}
    		return m_singer;
    
    		m_InstanceCount++;
    
    	}
    private:
    	static Singleton* m_singer;
    	static int m_InstanceCount;  //存放实例的个数
    };
    
    Singleton *Singleton::m_singer = NULL;
    int Singleton::m_InstanceCount = 1;//初始化的实例个数
    
    int main()
    {
    	Singleton* p1 = Singleton::GetInstance();
    	Singleton* p2 = Singleton::GetInstance();
    	Singleton* p3 = Singleton::GetInstance();
    	Singleton* p4 = Singleton::GetInstance();
    	Singleton* p5 = Singleton::GetInstance();
    	printf("p1=%x,p2=%x,p3=%x,p4=%x,p5=%x
    ", p1, p2, p3, p4, p5);
    	Singleton* p6 = Singleton::GetInstance();
    	Singleton* p7 = Singleton::GetInstance();
    	Singleton* p8 = Singleton::GetInstance();
    	Singleton* p9 = Singleton::GetInstance();
    	Singleton* p10 = Singleton::GetInstance();
    	printf("p6=%x,p7=%x,p8=%x,p9=%x,p10=%x
    ", p6, p7, p8, p9, p10);
    	system("pause");
    	return 0;
    }
    

    参考资料:
    1 《大话设计模式》
    2 https://blog.csdn.net/xiqingnian/article/details/41833033 大话设计模式C++实现-第21章-单例模式

  • 相关阅读:
    COGS 577 蝗灾 线段树+CDQ分治
    BZOJ 1305 二分+网络流
    BZOJ 1066 Dinic
    BZOJ 3544 treap (set)
    BZOJ 3940 AC自动机
    BZOJ 1503 treap
    BZOJ 3172 AC自动机
    BZOJ 2553 AC自动机+矩阵快速幂 (神题)
    BZOJ1901 ZOJ2112 线段树+treap (线段树套线段树)
    BZOJ 3196 线段树套平衡树
  • 原文地址:https://www.cnblogs.com/Manual-Linux/p/11101153.html
Copyright © 2011-2022 走看看