C++特殊类设计(不能被拷贝的类、只能在堆上创建对象的类、不能被继承的类、单例模式)

news2024/11/24 10:34:17

C++特殊类设计

在实际应用中,可能需要设计一些特殊的类对象,如不能被拷贝的类、只能在堆上创建对象的类、只能在栈上创建对象的类、不能被继承的类、只能创建一个对象的类(单例模式)

1. 不能被拷贝的类

拷贝只会发生在两个场景中:拷贝构造函数和赋值运算符重载。因此,让一个类禁止被拷贝,只需要让其拷贝构造函数和赋值运算符重载不能被调用即可

1.1 c++98做法

c++98通过将拷贝构造函数和赋值运算符重载只声明不定义,并将其访问权限设置为私有实现禁止被拷贝。

class CopyBan
{
private:
	CopyBan(const CopyBan& cb);
	CopyBan& operator=(const CopyBan & cb);
};

1.2 现代做法

使用c++11提供的delete关键字“删除”拷贝构造函数和赋值运算符重载。

class CopyBan
{
private:
	CopyBan(const CopyBan& cb) = delete;
	CopyBan& operator=(const CopyBan& cb) = delete;
};

2. 只能在堆上创建对象的类

2.1 直接法

要使一个类只能在堆上创建对象,思路是:

  1. 将类的构造函数和拷贝构造函数私有,防止别人调用拷贝在栈上生成对象。
  2. 再提供一个静态成员函数,在该静态成员函数内部完成堆对象的创建。
class HeapOnly
{
	static HeapOnly* Create()//静态解决“先有函数还是现有对象问题”
	{
		return new HeapOnly;
	}
private:
	HeapOnly(){}
};

但此不能完全封死在栈上创建对象,如果通过 Create()函数先创建一个堆上的对象,再使用默认拷贝构造拷贝堆上的对象,就能够实现在栈上创建对象。

HeapOnly* ho1 = HeapOnly::Create();
HeapOnly* ho2(ho1);

所以最后还需要封死通过拷贝构造创建栈上对象:

c++98:private:HeapOnly& HeapOnly(const HeapOnly& ho){}

c++11:HeapOnly& HeapOnly(const HeapOnly& ho)=delete;

2.2 私有析构函数法

设计不能被拷贝的类还有一种方法,通过私有化析构函数,让栈上对象无法在离开作用域时自动调用析构函数,因此在栈上的创建对象的代码都不能被编译通过。再设计一个 release()函数手动释放堆上的对象

class HeapOnly
{
public:
	static HeapOnly* Create()
	{
		return new HeapOnly;
	}
	void relase()
	{
		delete this;
	}
private:
	~HeapOnly(){}
};

3. 只能在栈上创建对象的类

要使一个类只能在栈上创建对象,思路是:

  1. 私有化构造函数
  2. 设计静态函数返回对象
class StackOnly
{
public:
	static StackOnly Create()
	{
		return StackOnly();
	}

	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;

private:
	StackOnly():_a(0){}
	int _a;
};

设计只能在栈上创建对象的类还要注意将new和delete删除,避免使用new通过拷贝构造创建堆上对象。且由于 Create()函数被设计成传值返回,不能直接通过删除拷贝构造实现(因为临时对象)。

删除new和delete的原理是,编译器默认生成一个new和一个delete,现将重载new和delete在类中重载,那么类对象会调用重载的new和重载的delete(重载后不再默认生成),但由于重载的new和重载的delete被删除,类对象在创建时便无法使用。

StackOnly so1 = StackOnly::Create();
StackOnly* so2 = new StackOnly(so1);

4. 不能被继承的类

要使一个类不能被继承,方法是:

  1. c++98:基类析构函数私有,派生类不能调用基类的构造函数,无法编译通过
  2. c++11:使用final关键字标记基类,表示该类不能被继承

5. 单例模式

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

单例模式有两种实现模式:

  1. 饿汉模式

  2. 懒汉模式

5.1 饿汉模式

饿汉形容程序对对象的需要比较紧迫,不管将来用不用,在程序启动时就马上先创建一个唯一的实例对象(一般在main函数之前创建)。

#include<iostream>
using namespace std;

class ehan
{
public:
	static ehan* GetInstance()
	{
		return &_e;
	}
	int SetInfo(int info)
	{
		_info = info;
		return _info;
	}
	ehan(const ehan& e) = delete;
	ehan& operator=(const ehan& e) = delete;
private:
	ehan(){}
	int _info;
	static ehan _e;//声明
};

ehan ehan::_e;//定义
int main()
{
	cout<<ehan::GetInstance()->SetInfo(1);
	return 0;
}

优先:简单

缺点:可能会导致进程启动慢,且如果有多个单例类使用饿汉模式,它们的对象实例启动顺序不确定

5.2 懒汉模式

懒汉模式可以完美解决饿汉模式的缺点,懒汉模式一般在第一次调用 GetInstance() 的时候创建单例对象。

#include<iostream>
using namespace std;

class lanhan
{
public:
	static lanhan* GetInstance()
	{
		static lanhan _lh;
		return &_lh;
	}
	int SetInfo(int info)
	{
		_info = info;
		return _info;
	}
	lanhan(const lanhan& e) = delete;
	lanhan& operator=(const lanhan& e) = delete;
private:
	lanhan(){}
	int _info;
};

int main()
{
	cout<<lanhan::GetInstance()->SetInfo(1);
	return 0;
}

这里在 GetInstance()里 定义了一个局部静态对象 static lanhan _lh; ,即使调用多次 GetInstance(),这个创建对象的代码也只会执行一次,但这种使用方法是c++11之后支持,且有线程安全风险

传统且线程安全方法:

#include<iostream>
#include<mutex>
using namespace std;

class lanhan
{
public:
	static lanhan* GetInstance()
	{
		if (_lh == nullptr)//双重检查保证线程安全
		{
			unique_lock<mutex> lock(_mtx);
			if (_lh == nullptr)
			{
				_lh = new lanhan;
			}
		}
		return _lh;
	}
	int SetInfo(int info)
	{
		_info = info;
		return _info;
	}
	lanhan(const lanhan& e) = delete;
	lanhan& operator=(const lanhan& e) = delete;
private:
	lanhan(){}
	int _info;
	static mutex _mtx;
	static lanhan* _lh;


};
lanhan* lanhan::_lh = nullptr;
mutex lanhan::_mtx;

int main()
{
	cout<<lanhan::GetInstance()->SetInfo(1);
	return 0;
}

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

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

相关文章

计算机网络 实验六 组网实验

一、实验目的 通过构造不同的网络拓扑结构图并进行验证&#xff0c;理解分组转发、网络通信及路由选择的原理&#xff0c;理解交换机和路由器在子网划分中的不同作用。 二、实验原理 组网实验是指将多个计算机通过网络连接起来&#xff0c;实现数据的共享和通信。 组网需要考虑…

LeetCode 力扣 热题 100道(八)相交链表(C++)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

Spring |(四)IoC/DI配置管理第三方bean

文章目录 &#x1f4da;数据源对象管理&#x1f407;环境准备&#x1f407;实现Druid管理&#x1f407;实现C3P0管理 &#x1f4da;加载properties文件&#x1f407;第三方bean属性优化&#x1f407;读取单个属性 学习来源&#xff1a;黑马程序员SSM框架教程_SpringSpringMVCMa…

鸿蒙NEXT开发案例:随机数生成

【引言】 本项目是一个简单的随机数生成器应用&#xff0c;用户可以通过设置随机数的范围和个数&#xff0c;并选择是否允许生成重复的随机数&#xff0c;来生成所需的随机数列表。生成的结果可以通过点击“复制”按钮复制到剪贴板。 【环境准备】 • 操作系统&#xff1a;W…

[译]Elasticsearch Sequence ID实现思路及用途

原文地址:https://www.elastic.co/blog/elasticsearch-sequence-ids-6-0 如果 几年前&#xff0c;在Elastic&#xff0c;我们问自己一个"如果"问题&#xff0c;我们知道这将带来有趣的见解&#xff1a; "如果我们在Elasticsearch中对索引操作进行全面排序会怎样…

小米14升级澎湃OS 2.0.6.VNCCNXM 记录

简介 11.23 小米14凌晨推送了澎湃2.0,还真是11月压轴的,不是内测申请的。 btw,什么时候才能有红米耳机连接的弹窗啊??为什么13都有,但是14没有? 系统更新推送 版本介绍 1.0.47 更新到 2.0.6.VNCCNXM,记录一些界面变化,应用问题和内存情况。 澎湃OS 2 更新 - 功能介…

【单点知识】基于PyTorch进行模型部署

文章目录 0. 前言1. 模型导出1.1 TorchScript1.1.1 使用 torch.jit.trace1.1.2 使用 torch.jit.script 1.2 ONNX1.2.1 导出为 ONNX 格式 1.3 导出后的模型加载1.3.1 加载 TorchScript 模型1.3.2 加载 ONNX 模型 2. 模型优化2.1 模型量化2.2 模型剪枝 3. 服务化部署3.1 Flask 部…

FreeRTOS——互斥信号量

一、为什么需要互斥信号量 前面的学习中&#xff1a; 调度锁、临界段不可避免的破坏了实时性&#xff0c;还有二值信号量存在这样的隐患——“优先级翻转” 优先级翻转 简单来说&#xff0c;就是由于信号量被低优先级任务占用&#xff0c;即使遇到高优先级任务&#xff0c;它…

前端-react(class组件和Hooks)

文章主要以Hooks为主,部分涉及class组件方法进行对比 一.了解react 1.管理组件的方式 在React中&#xff0c;有两种主要的方式来管理组件的状态和生命周期&#xff1a;Class 组件和 Hooks。 Class 组件&#xff1a; Class 组件是 React 最早引入的方式&#xff0c;它是基于…

Ngrok实现内网穿透(Windows)

Ngrok实现内网穿透&#xff08;Windows&#xff09; 什么是内网穿透&#xff0c;内网穿透有什么用 内网穿透&#xff08;NAT traversal&#xff09;是一种技术手段&#xff0c;使得位于内网或防火墙后面的设备能够通过外网访问。例如&#xff0c;如果你的计算机、服务器等设备…

如何使用Jest测试你的React组件

在本文中&#xff0c;我们将了解如何使用Jest&#xff08;Facebook 维护的一个测试框架&#xff09;来测试我们的React组件。我们将首先了解如何在纯 JavaScript 函数上使用 Jest&#xff0c;然后再了解它提供的一些开箱即用的功能&#xff0c;这些功能专门用于使测试 React 应…

力扣 三数之和-15

三数之和-15 class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {int temp 0;//定义一个二维vector数组vector<vector<int>> ans;int n nums.size();//对nums数组进行排序sort(nums.begin(), nums.end());//固定…

深度学习每周学习总结J6(ResNeXt-50 算法实战与解析 - 猴痘识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结ResNeXt基本介绍 1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1a;定义损失函数&…

Transformer架构笔记

Attention is All You Need. 3.Model Architecture 3.1 整体架构如图 3.2 Encoder与Decoder Encoder&#xff1a;由 N 6 N6 N6个相同的Block/Layer堆叠而成。每个Block有两个子层sub-layer&#xff1a;多头注意力和MLP&#xff08;FFN&#xff0c;前馈神经网络&#xff09;&…

【大数据学习 | Spark-Core】spark-shell开发

spark的代码分为两种 本地代码在driver端直接解析执行没有后续 集群代码&#xff0c;会在driver端进行解析&#xff0c;然后让多个机器进行集群形式的执行计算 spark-shell --master spark://nn1:7077 --executor-cores 2 --executor-memory 2G sc.textFile("/home/ha…

增量预训练(Pretrain)样本拼接篇

增量预训练&#xff08;Pretrain&#xff09;样本拼接篇 一、Pretrain阶段&#xff0c;为什么需要拼接拼接&#xff1f; 为了提高pretrain效率、拓展LLM最大长度&#xff0c;随机将若干条短文本进行拼接是pretrain阶段常见手段。 二、有哪些拼接方式&#xff1f; 拼接方式一…

【AI最前线】DP双像素sensor相关的AI算法全集:深度估计、图像去模糊去雨去雾恢复、图像重建、自动对焦

Dual Pixel 简介 双像素是成像系统的感光元器件中单帧同时生成的图像&#xff1a;通过双像素可以实现&#xff1a;深度估计、图像去模糊去雨去雾恢复、图像重建 成像原理来源如上&#xff0c;也有遮罩等方式的pd生成&#xff0c;如图双像素视图可以看到光圈的不同一半&#x…

从零开始-VitePress 构建个人博客上传GitHub自动构建访问

从零开始-VitePress 构建个人博客上传GitHub自动构建访问 序言 VitePress 官网&#xff1a;VitePress 中文版 1. 什么是 VitePress VitePress 是一个静态站点生成器 (SSG)&#xff0c;专为构建快速、以内容为中心的站点而设计。简而言之&#xff0c;VitePress 获取用 Markdown…

使用uniapp编写APP的文件上传

使用uniapp插件文件选择、文件上传组件&#xff08;图片&#xff0c;视频&#xff0c;文件等&#xff09; - DCloud 插件市场 实用效果&#xff1a; 缺陷是只能一个一个单独上传

【51单片机】红外遥控

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 红外遥控硬件电路 NEC协议编码编程实例LCD1602显示Data红外遥控控制扇叶转速 红外遥控 红外遥控是利用红外光进行通信的设备&#…