C++特殊类设计【特殊类 || 单例对象 || 饿汉模式 || 懒汉模式】

news2024/10/2 8:43:13

目录

1. 只在堆上创建的类

2. 只允许在栈上创建的类 

3. 不能被继承的类

4. 不能被拷贝的类

 5. 设计一个类,只能创建一个对象(单例对象)

饿汉模式

懒汉模式


嗨!收到一张超美的风景图,愿你每天都能顺心!

1. 只在堆上创建的类

实现方式:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
// 方法一
class HeapOnly
{
public:
	void Destory()
	{
		cout << "~HeapOnly" << endl;
		delete this;
	}

private:
	~HeapOnly()
	{}

	int i;
};
// 将析构函数设为私有,这样在栈中的对象(不需要手动析构),在栈结束时
// 无法自动调用析构函数。

// 方法二:构造函数私有,仅提供一个creat函数来进行构造
class HeapOnly_2
{
public:
	// static 添加原因:我们知道调用成员函数需要对象,但我们这时并没有对象,
	// 因此需要用static修饰。(先有鸡先有蛋问题)
	static HeapOnly_2*  creat_(int b) 
	{
		return new HeapOnly_2(b);
	}
	// 防止默认拷贝,赋值构造生成,在栈上创建
	HeapOnly_2(const HeapOnly_2& b) = delete;
	HeapOnly_2& operator=(const HeapOnly_2& b) = delete;
private:
	HeapOnly_2(int b)
		:i(b)
	{}

	int i;
};

int main()
{
	// HeapOnly it;
	HeapOnly* it = new HeapOnly;
	it->Destory();

	//HeapOnly_2 it2;
	HeapOnly_2* it2 = HeapOnly_2::creat_(1);
	//HeapOnly_2 it3 = *it2; 
	//HeapOnly_2 it3(*it2);
    delete it2
	return 0;
}

2. 只允许在栈上创建的类 

class StackOnly
{
public:
	static StackOnly creat_(int b)
	{
		return StackOnly(b); //意味着拷贝构造不能禁止 
	}

	// creat_返回值是右值,我们拷贝构造参数只设置右值,同时默认构造也不会产生
	StackOnly(StackOnly&& st)
		:_i(st._i)
	{}
	//但这也没有禁止,move一下就又可以了

private:
	// 构造一私有,堆栈上都无法构造创建
	StackOnly(int i)
		:_i(i){}

	StackOnly& operator=(const StackOnly& st) = delete;

	int _i;
};

方法同1类似,但这无法限制住,static对象的拷贝。

3. 不能被继承的类

C++98方式,将构造函数私有,派生类无法调用到构造函数导致无法被继承。

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
 static NonInherit GetInstance()
 {
 return NonInherit();
 }
private:
 NonInherit()
 {}
};

C++11新方式,在目标类添加关键词——final,表示该类无法被继承

class A  final
{
    // ....
};

4. 不能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此 想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可
C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
class CopyBan
{
    // ...
    
private:
    CopyBan(const CopyBan&);
    CopyBan& operator=(const CopyBan&);
    //...
};
原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

C++11
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{
    // ...
    CopyBan(const CopyBan&)=delete;
    CopyBan& operator=(const CopyBan&)=delete;
    //...
};

 5. 设计一个类,只能创建一个对象(单例对象)

设计模式:
设计模式(Design Pattern)是一套 被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。我们学过的有适配器,迭代器,包装器,以及扩展:工厂,观察者模式。下面的单例模式也是
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。单例模式有两种实现模式:

饿汉模式

就是说不管你将来用不用,程序启动时就先创建一个唯一的实例对象
class SingleCase
{
public:
	static SingleCase* GetInstace()
	{
		return _sc;
	}
	
	size_t GetSize()
	{
		return _vc.size();
	}

	void Add(const string& str)
	{
		_mtx.lock();
		_vc.push_back(str);
		_mtx.unlock();
	}
	
	void print()
	{
		for (auto& it : _vc)
		{
			cout << it << endl;
		}
	}

	SingleCase(const SingleCase& it) = delete;
	SingleCase& operator=(const SingleCase& it) = delete;
private:
	SingleCase()
	{}

	mutex _mtx;
	vector<string> _vc;
	static SingleCase* _sc;
};
// 声明定义分离,_sc是也在类成员中(能调用类成员),虽然看起来像在类外。
SingleCase* SingleCase::_sc = new SingleCase;

 饿汉的缺点:

1. 单例对象的构造可能会占用大量资源,对应用程序的打开比较慢。

2. 不适合多个单例对象之间存在依赖关系生成。如单例A,单例B,必须得先有A再有B,如果在同一个源文件中跟声明顺序相同;但如果不在同一个源文件中,产生顺序就不确定。

优点:代码简单

懒汉模式

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

class SingleCase_lazy
{
public:
	static SingleCase_lazy* GetInstace()
	{
		// 处理大多数情况,提高效率
		if (_sc == nullptr)
		{
			SingleCase_lazy::new_mtx.lock();
			// 两程序几乎同一时间进入该
			// 作用域时,再进行判断
			if (_sc == nullptr) 
			{
				// 加锁防止_sc发生覆盖
				_sc = new SingleCase_lazy;
			}
			SingleCase_lazy::new_mtx.unlock();
		}
		return _sc;
	}

	

	size_t GetSize()
	{
		return _vc.size();
	}

	void Add(const string& str)
	{
		_vmtx.lock();
		_vc.push_back(str);
		_vmtx.unlock();
	}

	void print()
	{
		for (auto& it : _vc)
		{
			cout << it << endl;
		}
	}

	 static void Destory()
	{
		new_mtx.lock();
		if (_sc)
		{
			cout << "delete _sc" << endl;
		}
		new_mtx.unlock();

		
		delete _sc;
	}

	class De
	{
	public:
		~De()
		{
			Destory();
		}
	};

	SingleCase_lazy(const SingleCase_lazy& it) = delete;
	SingleCase_lazy& operator=(const SingleCase& it) = delete;
private:
	SingleCase_lazy()
	{}
	static mutex new_mtx;
	mutex _vmtx;
	vector<string> _vc;
	static SingleCase_lazy* _sc; //无法显示调用析构,我们通过内部类,调用
};
SingleCase_lazy* SingleCase_lazy::_sc = nullptr;
mutex SingleCase_lazy::new_mtx; // 静态函数的锁
SingleCase_lazy::De _de;

总结:设计一个单例对象,我们需要满足:1. 把数据放一个类中,把这个类设计成单例类。 2. 保持进程唯一,需要将拷贝,赋值都禁止。 3. 设计模式是饿汉,还是懒汉

C++11静态成员初始化多线程安全问题

class SingleInstance {
public:
	static SingleInstance* GetInstance() {
		// 局部静态变量实现的懒汉式单例,C++11后线程安全
		// 在C++11之前,不能保证多线程安全
		static SingleInstance instance;
		return &instance;
	}
private:
	SingleInstance();
	~SingleInstance();
	SingleInstance(const SingleInstance& signal) = delete;
	SingleInstance& operator=(const SingleInstance& signal) = delete;
};

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

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

相关文章

Java中常用的集合及方法(2)

在Java&#xff08;JDK8&#xff09;中&#xff0c;集合&#xff08;Collection&#xff09;是数据结构的实现&#xff0c;用于存储和操作对象集合。 集合&#xff08;Collection&#xff09;中包含的一般类或接口&#xff1a; 在这其中呢&#xff0c;我们经常使用的其实就是L…

实验一:华为VRP系统的基本操作

1.1实验介绍 1.1.1关于本实验 本实验通过配置华为设备&#xff0c;了解并熟悉华为VRP系统的基本操作 1.1.2实验目的 理解命令行视图的含义以及进入离开命令行视图的方法 掌握一些常见的命令 掌握命令行在线帮助的方法 掌握如何撤销命令 掌握如何使用命令快捷键 1.1.3实验组网 …

数据结构(十)——头插法和尾插法建立单链表

&#x1f600;前言 在数据结构中&#xff0c;单链表是一种常见的数据结构&#xff0c;它由一个头节点和若干个数据节点组成。创建单链表的过程可以通过头插法或尾插法来实现。头插法是将新节点插入到链表的头部&#xff0c;而尾插法是将新节点插入到链表的尾部。本文将介绍头插…

qt 格式化打印 日志 QMessagePattern 格式词法语法及设置

一、qt源码格式化日志 关键内部类 QMessagePattern qt为 格式化打印日志 提供了一个简易的 pattern(模式/格式) 词法解析的简易的内部类QMessagePattern,作用是获取和解析自定义的日志格式信息。 该类在qt的专门精心日志操作的源码文件Src\qtbase\src\corelib\global\qloggi…

ChatGPT预训练的奥秘:大规模数据、Transformer架构与自回归学习【文末送书-31】

文章目录 ChatGPT原理与架构ChatGPT的预训练ChatGPT的迁移学习ChatGPT的中间件编程 ChatGPT原理与架构&#xff1a;大模型的预训练、迁移和中间件编程【文末送书-31】 ChatGPT原理与架构 近年来&#xff0c;人工智能领域取得了巨大的进展&#xff0c;其中自然语言处理&#xf…

微信小程序onLoad加载定义好的函数

这里小程序开发中容易犯的错误-1 给客户做一个程序。需要在页面加载的时候在onLoad(options){}中加载定义好的函数&#xff0c;代码如下 onLoad(options) {get_week_()},运行时老报错 后来修改为正确的代码 onLoad(options) {this.get_week_()//必须加this},再尝试运行&#x…

java学习(Arrays类和System类)

目录 一.Arrays类 二.System常见方法 一.Arrays类 Arrays包含了一系 列静态方法&#xff0c;用于管理或操作数组&#xff08;比如排序和搜索&#xff09; Integer[] s{1,2,3};//1.Arrays.toString方法&#xff0c;遍历数组//2.Arrays.sortArrays.sort(s);//默认排序&#xf…

幕译--本地字幕生成与翻译--Whisper客户端

幕译–本地字幕生成与翻译 本地离线的字幕生成与翻译&#xff0c;支持GPU加速。可免费试用&#xff0c;无次数限制 基于Whisper&#xff0c;希望做最好的Whisper客户端 功能介绍 本地离线&#xff0c;不用担心隐私问题支持GPU加速支持多种模型支持&#xff08;中文、英语、日…

Anaconda prompt运行打开jupyter notebook 指令出错解决方案

一、打不开jupyter notebook网页 报错如下&#xff1a; Traceback (most recent call last): File “D:\anaconda3\lib\site-packages\notebook\traittypes.py”, line 235, in _resolve_classes klass self._resolve_string(klass) File “C:\Users\DELL\AppData\Roaming\Py…

idea2023版使用废弃类没有删除线处理方法

idea2023版使用废弃类没有删除线处理方法 新版Idea使用废弃类时,默认是黄色警告处理方法1. 打开file -> setting2. 编辑(Editor) -> 检查(Inspections) -> 搜索Deprecated API usage 新版Idea使用废弃类时,默认是黄色警告 处理方法 1. 打开file -> setting 2. 编…

Linux系统架构----LNMP平台部署中部署wordpress

Linux系统架构----LNMP平台部署中部署wordpress 一、LNMP的概述 LNMP为Linux平台&#xff0c;Nginx web服务软件&#xff0c;mysql数据库软件&#xff0c;PHP编辑语言LNMP系统架构相对于LAMP的优点是LNMP比较节省内存&#xff0c;主要支持静态请求&#xff0c;但在访问量大的…

Excel中的subtotal函数

咋一看subtotal函数&#xff0c;感觉很陌生&#xff0c;但其实这个函数我们有可能在无意中碰到过。如果经常有求和需求的小伙伴&#xff0c;碰到这个函数概率比较大&#xff0c;只要在筛选的情况下&#xff0c;进行自动求和&#xff0c;都会出现这个公式。那这个函数是怎么用的…

CVE-2021-31440:eBPF verifier __reg_combine_64_into_32 边界更新错误

文章目录 前言漏洞分析构造 vuln reg 漏洞利用漏洞修复参考 前言 影响版本&#xff1a;Linux 5.7 ~ 5.11.20 8.8 编译选项&#xff1a;CONFIG_BPF_SYSCALL&#xff0c;config 所有带 BPF 字样的编译选项。General setup —> Choose SLAB allocator (SLUB (Unqueued Allocat…

初识Python(helloworld、海洋距离单位换算、打印名片、文本进度条、判断水仙花数)

一、Python3的安装&#xff0c;IDLE的使用&#xff1a;使用print函数输出”hello world”&#xff1b; 二、 PyCharm的安装与使用&#xff1a;创建”hello_world.py”文件并使用print函数输出”hello world” 三、海洋单位距离换算 要求&#xff1a;运行代码&#xff0c;控制台…

PostgreSQL中In, Exists在SQL查询中到底有无区别

前言 SQL查询当中&#xff0c;In和Exists子查询到底有无区别&#xff1f;记得很多年以前&#xff0c;确实是有相关的使用戒条的&#xff0c;或者说存在一些使用的惯用法。试图完全抹开两者的区别&#xff0c;就有点过了。 两者的主要区别&#xff1a; 从目的上讲&#xff0c…

三栏布局的实现方法

1. 什么是三栏布局 常见的一种页面布局方式&#xff0c;将页面分为左栏、中栏和右栏左右两侧的盒子宽度固定&#xff0c;中间的盒子会随屏幕自适应一般中间放主体内容&#xff0c;左右两边放辅助内容 2. 如何实现三栏布局 2.1 弹性布局 将最外层盒子设为弹性布局&#xff0…

截图软件Snipaste在截图文本无法输入C

1、现象&#xff1a; 远程桌面使用截图软件Snipaste&#xff0c;在输入文本时C键无效&#xff0c;猜测是优先级问题导致 2、解决方法&#xff1a; 在右下角图标上鼠标右键&#xff0c;点击首选项 打开配置文件 在[Snip]下粘贴&#xff1a;do_not_omit_synthesized_ctrue Ctrl…

计算两帧雷达数据之间的变换矩阵

文章目录 package.xmlCMakeLists.txtpoint_cloud_registration.cc运行结果 package.xml <?xml version"1.0"?> <package format"2"><name>point_cloud_registration</name><version>0.0.0</version><descriptio…

【嵌入式——QT】MDI应用程序设计

MDI应用程序就是在主窗口里创建多个同类型的MDI子窗口&#xff0c;这些MDI子窗口在主窗口里显示&#xff0c;并享受主窗口上的工具栏和菜单等操作功能&#xff0c;主窗口上的操作都针对当前活动的MDI子窗口进行。 图示 代码示例 QWMainWindow.h #ifndef QWMAINWINDOW_H …

Jetpack Navigation

1.Navigation的诞生与优势 这个留到Compose去学