单例模式(饿汉模式 懒汉模式)与一些特殊类设计

news2024/9/29 1:23:39

文章目录

一、不能被拷贝的类

二、只能在堆上创建类对象

三、只能在栈上创建类对象

四、不能被继承的类

五、单例模式

5、1 什么是单例模式

5、2 什么是设计模式

5、3 单例模式的实现

5、3、1 饿汉模式

5、3、1 懒汉模式


🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️

👀 专栏:C++ 👀

💥 标题:特殊类的设计💥

 ❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️ 

一、不能被拷贝的类

  一个类拷贝都是由拷贝构造来完成的。拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。代码如下:

class NonCopyableClass {
public:
    NonCopyableClass() {}

private:
    // 禁用拷贝构造函数
    NonCopyableClass(const NonCopyableClass&) = delete;

    // 禁用拷贝赋值运算符
    NonCopyableClass& operator=(const NonCopyableClass&) = delete;
};

int main() {
    NonCopyableClass obj1;

    // 编译错误,不能拷贝对象
    // NonCopyableClass obj2(obj1);
    //NonCopyableClass obj3 = obj1;

    return 0;

  在C++98中,将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有也是可以的。

二、只能在堆上创建类对象

  我们在栈和静态区创建对象时,都需要调用构造函数来初始化。同时,为了防止拷贝和赋值在栈空间上,所以将默认构造、拷贝构造、赋值重载都禁用了。

  那我们可以在类内部单独提供一个动态申请对象的静态成员函数。具体代码如下:

class HeapOnly
{
public:
	// 提供一个公有的,获取对象的方式,对象控制是new出来的
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

	// 防拷贝
	HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;

    void Destroy() {
        delete this;
    }
private:
	// 构造函数私有
	HeapOnly()
		:_a(0)
	{}
private:
	int _a;
};

int main()
{
	HeapOnly* hp = HeapOnly::CreateObj();
	//HeapOnly copy(*hp);

	hp->Destroy();

	return 0;
}

三、只能在栈上创建类对象

  为了防止在堆上或者静态区申请对象,构造函数应该私有。可以在类内部提供一个返回栈对象的方法。代码如下:

class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}

private:
	// 构造函数私有
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

   但是,此时还能拷贝构造和赋值呢!!!那么能够禁用掉拷贝构造吗?答案是不能的。原因是我们通过传值返回的栈对象,此时必须需要拷贝。不能够传引用返回,因为是局部变量。出了函数就会被销毁。我们最多的就是禁用掉new的使用,具体代码如下:

class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}

	// 不能防拷贝
	//StackOnly(const StackOnly& st) = delete;
	StackOnly& operator=(const StackOnly& st) = delete;
	void* operator new(size_t n) = delete;
private:
	// 构造函数私有
	StackOnly()
		:_a(0)
	{}
private:
	int _a;
};

int main()
{
	StackOnly st1 = StackOnly::CreateObj();

	// 拷贝构造
	static StackOnly copy2(st1);
	//StackOnly* copy3 = new StackOnly(st1);

	return 0;
}

四、不能被继承的类

  一个类不能被继承有两种方法:

  • C++98方式:构造函数私有化,派生类中调不到基类的构造函数。则无法继承。
    class NonInherit
    {
    public:
    	static NonInherit GetInstance()
    	{
    		return NonInherit();
    	}
    private:
    	NonInherit()
    	{}
    };
  • C++11方法:final关键字,final修饰类,表示该类不能被继承。
    class A final
    {
        // ....
    };

五、单例模式

5、1 什么是单例模式

  单例模式是一种设计模式,用于确保在整个应用程序中只存在一个特定类的实例对象,该实例对象被所有程序模块共享。其主要目的是限制类的实例化操作,以确保在任何情况下都只能获得同一个实例。

  比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

5、2 什么是设计模式

  设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

  使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

5、3 单例模式的实现

  单例模式实现的方法有两种:饿汉模式、懒汉模式。我们先来看一下饿汉模式的实现。

5、3、1 饿汉模式

  所谓饿汉模式,就是在main函数之前,就会创建出对象。并不会考虑到你是否要用这个对象。那怎么实现呢?

  首先,为了保证只能实例出一份对象,就必须把构造函数私有化。其次,需要在自己的类内部定义一个静态的类对象或者类指针。然后在类内部定义一些静态成员函数来初始化、获取对象/指针等操作。具体代码如下:

//class MemoryPool
//{
//public:
//
//private:
//	// 构造函数私有化
//	MemoryPool()
//	{}
//
//	char* _ptr = nullptr;
//	// ...
//
//	static MemoryPool _inst; // 声明
//};
//
 定义
//MemoryPool MemoryPool::_inst;

class MemoryPool
{
public:
	static MemoryPool* GetInstance()
	{
		return _pinst;
	}

	void* Alloc(size_t n)
	{
		void* ptr = nullptr;
		// ....
		return ptr;
	}

	void Dealloc(void* ptr)
	{
		// ...
	}

    MemoryPool(MemoryPool& my) = delete;
    MemoryPool& operator= (MemoryPool& my) = delete;
private:
	// 构造函数私有化
	MemoryPool()
	{}

	char* _ptr = nullptr;
	// ...

	static MemoryPool* _pinst; // 声明
};

// 定义
MemoryPool* MemoryPool::_pinst = new MemoryPool;

int main()
{
	//MemoryPool pool1;
	//MemoryPool pool2;

	void* ptr1 = MemoryPool::GetInstance()->Alloc(10);
	MemoryPool::GetInstance()->Dealloc(ptr1);
}

  我们来分析一下:当类内定义的是静态成员时,需要在类外进行初始化。那么在类外能够调用私有的构造函数进行初始化吗?答案是可以的!因为该成员是属于类内部的私有成员,只不过是在类外进行的初始化。

  那么能在类内定义非静态类对象吗?答案是不可以的,这本身就是语法错误。其次静态成员变量并不属于某个对象,而是属于整个类

  总结饿汉模式(Eager Initialization) 的优点

  • 实现简单,线程安全。在类加载时就创建了实例,没有线程安全问题。
  • 可以保证在任何时候获取到同一个实例。

  缺点

  • 类加载时即创建实例,尤其是在实例初始化比较耗时的情况下,会影响到程序的启动速度。
  • 如果该实例一直没有被使用,则会造成内存的浪费。
  • 当有多个单例对象时,饿汉模式无法很好的控制其初始化先后顺序。当然,在一个文件內部还好,在多个文件中就无法确定静态成员的初始化顺序。

5、3、1 懒汉模式

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

class MemoryPool
{
public:
	static MemoryPool* GetInstance()
	{
		if (_pinst == nullptr)
		{
			_pinst = new MemoryPool;
		}

		return _pinst;
	}

	void* Alloc(size_t n)
	{
		void* ptr = nullptr;
		// ....
		return ptr;
	}

	void Dealloc(void* ptr)
	{
		// ...
	}

	// 实现一个内嵌垃圾回收类    
	class CGarbo {
	public:
		~CGarbo()
		{
			if (_pinst)
				delete _pinst;
		}
	};
    MemoryPool(MemoryPool& my) = delete;
    MemoryPool& operator= (MemoryPool& my) = delete;
private:
	// 构造函数私有化
	MemoryPool()
	{
		// ....
	}
    
	char* _ptr = nullptr;
	// ...

	static MemoryPool* _pinst; // 声明
};

// 定义
MemoryPool* MemoryPool::_pinst = nullptr;

// 回收对象,main函数结束后,他会调用析构函数,就会释放单例对象
static MemoryPool::CGarbo gc;

  总结懒汉模式(Lazy Initialization) 优点

  • 延迟实例化,只有第一次调用获取实例的方法时才会创建对象,避免了资源浪费。
  • 可以控制对象的初始化顺序。
  • 不影响程序的启动速度。

  缺点

  • 不是线程安全的,如果多个线程并发地调用获取实例的方法,可能会创建多个实例。
  • 在多线程环境下,需要额外的同步措施来保证线程安全,增加了复杂性开销。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1025830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023-09-20 Android CheckBox 让文字显示在选择框的左边

一、CheckBox 让文字在选择框的左边 &#xff0c;在布局文件里面添加下面一行就可以。 android:layoutDirection"rtl" 即可实现 android:paddingStart"10dp" 设置框文间的间距 二、使用的是left to right <attr name"layoutDirection">&…

无代码开发和低代码开发的本质区别

目录 一、两者的概念区别 二、两者面向的人群不同 三、集成能力的区别 四、扩展能力的区别 五、选购建议 无代码和低代码开发都是目前新兴的一种软件开发方式。 一、两者的概念区别 低代码开发&#xff08;Low-Code Development&#xff09;是一种通过使用图形界面和预先构建的…

机器学习入门教学——损失函数(最小二乘法)

1、前言 我们在训练神经网络时&#xff0c;最常用到的方法就是梯度下降法。在了解梯度下降法前&#xff0c;我们需要了解什么是损失(代价)函数。所谓求的梯度&#xff0c;就是损失函数的梯度。如果不知道什么是梯度下降的&#xff0c;可以看一下这篇文章&#xff1a;机器学习入…

【c语言】详解结构体

目录 什么是结构体&#xff1f;结构体的声明结构体变量的创建和初始化匿名结构体类型结构体的自引用结构体的初始化普通初始化指定初始化 结构体内存对齐对齐规则默认对齐数的修改 结构体传参 什么是结构体&#xff1f; 在学习每个类型之前我们需要了解其存在的意义&#xff0…

简单几个配置 Go 实现敏感数据脱敏,可以自定义数据脱敏规则(附完整实现源码)

简单几个配置 Go 实现敏感数据脱敏,可以自定义数据脱敏规则(附完整实现源码)。 介绍 为了保障企业的数据安全和隐私安全,godlp 提供了一系列针对敏感数据的识别和处置方案,其中包含敏感数据识别算法,数据脱敏处理方式,业务自定义的配置选项和海量数据处理能力。godlp 能…

全流程WRF高精度气象模拟技术及在地学领域应用教程

详情点击公众号链接&#xff1a;全流程WRF高精度气象模拟技术及在地学领域应用教程 前沿 气候是多个领域&#xff08;生态、水资源、风资源及碳中和等问题&#xff09;的主要驱动因素&#xff0c;合理认知气候变化有利于解释生态环境变化机理及过程&#xff0c;而了解现在、未…

【Spatial-Temporal Action Localization(六)】论文阅读2021年

文章目录 1. MultiSports: A Multi-Person Video Dataset of Spatio-Temporally Localized Sports Actions摘要和结论引言&#xff1a;针对痛点和贡献数据特点 2. Actor-Context-Actor Relation Network for Spatio-Temporal Action Localization摘要和结论引言&#xff1a;针对…

跨平台编译C代码问题之ARM平台char类型导致异常及其解决措施

目录 环境设备&#xff1a; 背景现象&#xff1a; 1、x86下的结果输出和打印输出 2、arm下的结果输出和打印输出 原因分析&#xff1a; 解决措施&#xff1a; 环境设备&#xff1a; x86的ubuntu、arm的麒麟 背景现象&#xff1a; 由于项目需要&#xff0c;需要将代码移植…

Linux编辑器-gcc的使用

一&#xff1a;背景知识 1.预处理&#xff08;头文件展开、去注释、宏替换、条件编译&#xff09; 2.编译&#xff08;由C生成汇编&#xff09; 3.汇编&#xff08;生成及其可识别代码&#xff09; 4.连接&#xff08;生成可执行文件或库文件&#xff09; 二&#xff1a;gcc…

解决ModuleNotFoundError: No module named ‘diffusers.models.cross_attention‘

目录 项目场景: 问题描述 原因分析: 解决方案: 方案一:

如何取消显示Notepad++每行显示的CRLF符号

新电脑中重新安装了Nodepad&#xff0c;打开记事本后发现出现了许多黑底的CR|LF标记&#xff0c;特别碍眼。 如何取消呢&#xff1f; 视图 -> 显示符号 -> 取消勾选 显示行尾符操作步骤 预期效果

秦丝9周年 | 各行业实体生意如何实现数字化转型?

近期&#xff0c;北京、深圳、天津、重庆等全国27个省都在推进“一刻钟便民生活圈”——以社区居民为服务对象&#xff0c;在步行15分钟左右的范围内&#xff0c;满足居民日常生活基本消费和品质消费。 而各行业的实体店是这个“圈”中的重要组成部分&#xff0c;很多入驻的实…

jupyter notebook进不去指定目录怎么办?

首先激活你要使用的虚拟环境 刚开始是现在 (base) C:\Users\lenovo>目录下 直接输入你想进入的盘 (base) C:\Users\lenovo>e:此时再cd (base) C:\Users\lenovo>cd E:\tim\learn_pytorch 就可以进入了 安装3.4.1.15问题 已经有了最新python版本的虚拟环境&#…

6-1 汉诺塔

汉诺&#xff08;Hanoi&#xff09;塔问题是一个经典的递归问题。 设有A、B、C三个塔座&#xff1b;开始时&#xff0c;在塔座A上有若干个圆盘&#xff0c;这些圆盘自下而上&#xff0c;由大到小地叠在一起。要求将塔座A上的圆盘移到塔座B上&#xff0c;并仍按同样顺序叠放。在…

微服务是个坏主意吗?

曾几何时&#xff0c;我记得我的手指疯狂地敲打键盘&#xff0c;与庞大而杂乱的代码库搏斗。那是巨石的时代&#xff0c;代码就像古老的城堡一样&#xff0c;由一块块石头砌成一个令人印象深刻的庞然大物。 几年过去了&#xff0c;时代变了。开发人员口中的流行语变成了“微服…

TS同时打包和监视所有ts文件或只指定ts文件

当我们项目中ts文件较多时&#xff0c;我们如何直接打包所有ts文件为js文件&#xff1f;而不是使用tsc 文件名 一个一个去打包文件 一、配置tsconfig.json文件 创建一个tsconfig.json文件&#xff0c;该文件中不需要配置任何信息 二、控制台输入打包命令 在控制台输入如下代…

手机机型响应式设置2

window.screen.height&#xff1a;屏幕高度 window.innerHeight&#xff1a;视口高度&#xff08;去除浏览器头尾的高度&#xff09; document.body.clientHeight&#xff1a;内容高度 vh&#xff1a;网页视口高度的1/100 vw&#xff1a;网页视口宽度的1/100 vmax&#xff…

2万多条汉字笔画笔顺查询ACCESS\EXCEL数据库

发现很多新华字典类的数据都没有笔顺的相关数据&#xff0c;因此就找了一下笔顺查询相关&#xff0c;发现有两个模式&#xff0c;一种是每个字每个笔画都有一张图片&#xff08;很庞大的图片数据量&#xff09;&#xff1b;一种是笔画图片文件显示型&#xff0c;比如今天采集的…

Vue的详细教程--基础语法【下】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Vue的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.事件处理器 ①.stop ②.prevent ③.on…

消费盲返模式:一种让消费者和商家都受益的新型消费返利模式

消费盲返是一种新型的消费返利模式&#xff0c;它的核心思想是&#xff1a;消费者在平台购买商品后&#xff0c;可以获得后续一定数量的订单的部分利润作为奖励。这样&#xff0c;消费者不仅可以享受商品的优惠&#xff0c;还有可能赚取更多的钱。 这种模式对于平台和消费者都有…