• 【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单例再创建初始化,此时就不能够使用饿汉模式,因为无法保证其初始化顺序,而使用懒汉模式则可以手动控制

    懒汉模式

    优点

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

    缺点

    • 需要考虑判断的线程安全问题,效率会有所降低
    • 编写较为复杂
  • 相关阅读:
    【笔记】Nginx(5)七层负载均衡
    怎么学编程效率高,编程练习网站编程软件下载,中文编程开发语言工具下载
    Leetcode——岛屿的最大面积
    算法----删掉一个元素以后全为 1 的最长子数组
    测评瑞萨RZ/G2L存储读写速度与网络
    Linux文件系统及命令 | 实用操作指令汇总
    回归预测 | MATLAB实现基于BP-Adaboost的BP神经网络结合AdaBoost多输入单输出回归预测
    Matlab:对函数调用结果进行索引
    论如何优雅的注入Java Agent内存马
    【ASM】字节码操作 工具类与常用类 InstructionAdapter 介绍
  • 原文地址:https://blog.csdn.net/xiaomage1213888/article/details/125458843