• 【C++】设计模式之单例模式


    概念

    单例模式一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全
    局访问点该实例被所有程序模块共享

    单例模式的实现分为饿汉模式和懒汉模式


    饿汉模式

    程序启动时创建一个唯一的实例对象去加载所有的资源(预加载)

    • 构造函数私有化
    • 防拷贝
    • 在类内创建一个静态对象,静态对象是属于类的,唯一的一份,符合单例模式的要求
    • 提供一个静态接口,返回对象的指针或者引用

    在静态区创建对象

    class Singleton {
    public:
    	static Singleton& GetInstance(){
    		return _inst;
    	}
    
    private:
    	//构造函数私有化
    	Singleton(){}
    
    	//防拷贝
    	Singleton(const Singleton&) = delete;
    	Singleton operator=(Singleton) = delete;
    
    	//创建静态对象,保证只有一个对象
    	static Singleton _inst;
    };
    Singleton Singleton::_inst;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在堆上创建对象

    class Singleton {
    public:
    	static Singleton* GetInstance() {
    		return _inst;
    	}
    
    private:
    	//构造函数私有化
    	Singleton() {}
    
    	//防拷贝
    	Singleton(const Singleton&) = delete;
    	Singleton operator=(Singleton) = delete;
    
    	//创建静态对象,保证只有一个对象
    	static Singleton* _inst;
    };
    Singleton* Singleton::_inst = new Singleton;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    饿汉模式是在类加载阶段就已经完成了对象的初始化,所以饿汉模式不存在线程安全问题

    但是正因为如此,如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式延迟加载)更好


    懒汉模式

    堆区版本的懒汉模式

    • 构造函数私有化
    • 防拷贝
    • 创建一个静态对象指针,提供一个静态接口
    • 调用这个静态接口的时候,如果还没有创建对象,就new一个对象返回对象地址,如果对象已经存在,就什么都不做
    • 在进行对象是否存在的判断时,要保证线程安全
    • 如果要对创建的对象进行释放,可以添加一个静态del函数,或者添加一个垃圾回收类,在对象生命周期结束时自动释放资源
    class Singleton {
    public:
    	static Singleton* GetInstance() {
    
    		//双检查,在没有对象创建的时候才去获取锁,创建对象
    		if (_inst == nullptr) {
    			_mtx.lock();
    			if (_inst == nullptr) {
    				_inst = new Singleton;
    			}
    			_mtx.unlock();
    		}
    		return _inst;
    	}
    
    	static void DelInstance() {
    
    		_mtx.lock();
    		if (_inst) {
    			delete _inst;
    			_inst = nullptr;
    		}
    		_mtx.unlock();
    	}
    
    	// 实现一个内嵌垃圾回收类
    	class CGarbo {
    	public:
    		~CGarbo() {
    			if (_inst) {
    				delete _inst;
    				_inst = nullptr;
    			}
    		}
    	};
    
    private:
    	//构造函数私有化
    	Singleton() {}
    
    	//防拷贝
    	Singleton(const Singleton&) = delete;
    	Singleton operator=(Singleton) = delete;
    
    	//创建静态对象,保证只有一个对象
    	static Singleton* _inst;
    	static mutex _mtx;
    	static CGarbo _cg;
    };
    Singleton* Singleton::_inst = nullptr;
    mutex Singleton::_mtx;
    Singleton::CGarbo Singleton::_cg;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    静态区版本的懒汉模式

    class Singleton {
    public:
    	static Singleton& GetInstance() {
    
    		static Singleton inst;
    		return inst;
    	}
    
    private:
    	//构造函数私有化
    	Singleton() {}
    
    	//防拷贝
    	Singleton(const Singleton&) = delete;
    	Singleton operator=(Singleton) = delete;
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这种方法不必考虑线程安全问题,编写十分简单

    这种方式有一些缺陷

    • 对象创建在静态区,如果对象太大就不适合用这种方法
    • 无法手动释放资源,只有对象生命周期到期时才会自动释放

    饿汉模式和懒汉模式的优缺点

    饿汉模式

    优点

    • 简单

    缺点

    • 程序启动时候就要加载资源,如果需要初始化的资源过大,就会导致程序的启动速度变慢,影响用户体验
    • 如果有多个单例类,假设之间存在着依赖关系,例如B依赖A,要求A单例先创建初始化,B单例再创建初始化,此时就不能够使用饿汉模式,因为无法保证其初始化顺序,而使用懒汉模式则可以手动控制

    懒汉模式

    优点

    • 延迟加载,程序启动的时候不会初始化类不会影响程序的启动速度
    • 可以手动控制类的初始化顺序

    缺点

    • 需要考虑判断的线程安全问题,效率会有所降低
    • 编写较为复杂
  • 相关阅读:
    Visual Studio 2019编译HTKlib
    Azure Neural TTS 持续上新,助力企业开拓小语种市场
    Arcpy新增随机高程点、空间插值及批量制图
    【每日训练】进制转换
    React 之 react-router-dom
    【手把手】光说不练假把式,这篇全链路压测实践探索
    MySQL数据库基本操作2
    百度竞价 - 百度单页竞价推广项目实操教程分享
    浅谈Vue中render函数
    Go channel被关闭时的广播机制,以及遍历未关闭channel时会导致死锁阻塞问题
  • 原文地址:https://blog.csdn.net/xiaomage1213888/article/details/125458843