【C++】—— 特殊类设计
目录
序言
(一)设计一个不能被拷贝的类
(二)设计一个只能在堆上创建对象的类
(三)设计一个只能在栈上创建对象的类
(四)设计一个不能被继承的类
总结
序言
特殊类设计是指在面向对象编程中,根据特定需求或情况,创建具备特殊功能或属性的类。特殊类设计旨在解决特定问题或满足特殊需求,使代码更加灵活和可扩展。
(一)设计一个不能被拷贝的类
拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
接下来,我们分别从C++98和C++11的两种场景去看二者是如何实现:
【C++98】:将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
- 代码如下:
class CopyBan
{
private:CopyBan(const CopyBan&); // 声明拷贝构造函数为私有CopyBan& operator=(const CopyBan&); // 声明拷贝赋值运算符为私有public:CopyBan() {} // 默认构造函数
};
【解释说明】
- 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
- 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
使用该类时,只需简单地继承它即可:
class MyClass : public CopyBan{// 类的定义
};
【C++11】:C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
- 代码如下:
class CopyBan
{//.....CopyBan(const CopyBan&) = delete; // 删除拷贝构造函数CopyBan& operator=(const CopyBan&) = delete; // 删除拷贝赋值运算符//.....
};
【解释说明】
- 在上面的示例中,我们定义了一个名为 CopyBan 的类。通过将拷贝构造函数和拷贝赋值运算符声明为 delete ,我们禁用了对象的拷贝功能;
- 这样一来,任何试图拷贝 CopyBan 类型对象的操作都会在编译时引发错误。
同样的使用该类时,只需简单地继承它即可:
class MyClass : public CopyBan{// 类的定义
};
- 在这个示例中,MyClass 继承了CopyBan 类。但是,由于基类CopyBan 禁用了拷贝构造函数和赋值运算符函数,所以无法对 MyClass 进行拷贝操作。
【小结】
- 使用这种设计,你可以确保该类的实例不会被拷贝,从而避免不必要的对象复制和可能引发的错误。
(二)设计一个只能在堆上创建对象的类
实现方式:
- 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
- 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
【C++98】 :
- 代码如下:
class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly();}
private:HeapOnly() {} // 私有化默认构造函数// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&);
};
【C++11】:
- 代码如下:
class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly();}
private:HeapOnly() {} // 私有化默认构造函数// C++11HeapOnly(const HeapOnly&) = delete;
};
【解释说明】
- 在 C++11 标准之后,可以使用
= delete来删除复制构造函数,而不仅仅是将其声明为私有; - 这种方式更加清晰明了,使得代码更具可读性和表达性。因此,推荐使用 C++11 及以上版本的标准来实现这样的需求。
这样,你就可以使用下面的代码在堆上创建 HeapOnly 类对象:
HeapOnly* obj = HeapOnly::CreateObject();
请确保在使用完对象后手动调用 delete 来释放内存:
delete obj;
【小结】
- 这样设计的类将只能在堆上创建对象,并且无法通过拷贝或赋值的方式创建新对象,从而确保了对象的唯一性和创建方式的约束。
(三)设计一个只能在栈上创建对象的类
实现方法:
- 要设计一个只能在栈上创建对象的类,可以使用私有的析构函数和公有的静态成员函数来实现。
【C++98】 :
- 代码如下:
class StackOnly
{
public:static StackOnly CreateObj() {return StackOnly();}private:StackOnly() {} // 私有化默认构造函数~StackOnly() {} // 私有化析构函数
};
【解释说明】
- 在上面的示例中,我们将默认构造函数和析构函数私有化。这意味着外部无法直接实例化或销毁 StackOnly 类的对象。
- 为了能够创建对象,我们提供了一个名为 CreateObj 的公有静态成员函数。该函数返回一个 StackOnly 类型的对象。
使用代码示例:
StackOnly obj = StackOnly::CreateObj();
- 这样就确保了 StackOnly 类的对象只能在栈上创建,因为无法直接访问私有的默认构造函数;
- 对象的析构由编译器自动处理(不需要手动调用
delete释放内存),当对象超出作用域时会自动调用析构函数进行资源的释放。
【C++11】:可以使用删除特殊成员函数以及阻止使用new和delete操作符的方式来实现只能在栈上创建对象的类。
- 代码如下:
class StackOnly
{
public:StackOnly() = default; // 允许默认构造函数// 删除拷贝构造函数和赋值运算符函数StackOnly(const StackOnly&) = delete;StackOnly& operator=(const StackOnly&) = delete;// 禁止使用new和delete操作符void* operator new(size_t) = delete;void operator delete(void*) = delete;
};
使用代码示例:
StackOnly obj1; // 在栈上创建对象// 下面的代码将导致编译错误,因为拷贝构造函数被删除
// StackOnly obj2 = obj1;// 下面的代码将导致编译错误,因为赋值运算符函数被删除
// StackOnly obj3;
// obj3 = obj1;// 下面的代码将导致编译错误,因为使用了删除的new运算符
// StackOnly* ptr = new StackOnly;// 下面的代码将导致编译错误,因为使用了删除的delete运算符
// delete ptr;
【解释说明】
- 这样设计的类将只能在栈上创建对象,并且无法通过拷贝或赋值的方式创建新对象,同时禁止使用
new和delete来分配和释放对象的内存,从而确保了对象的唯一性和创建方式的限制。
除了上述方法之外,还有一种比较奇特的方式,可以用于实现只能在栈上创建对象的类。这种方式是通过定义一个私有化的
operator new和operator delete函数来实现,无需删除构造函数和析构函数。
- 代码如下:
class StackOnly
{
public:// 在 public 区域声明 operator new 和 operator delete 函数static void* operator new(size_t size) = delete; // 删除 operator new 函数static void operator delete(void* ptr) noexcept = delete; // 删除 operator delete 函数private:// 私有化所有构造、析构函数,包括默认构造函数StackOnly() {}StackOnly(const StackOnly&) {}~StackOnly() {}
};
【解释说明】
- 在上面的示例中,将构造函数和析构函数都设为了私有的,防止对象在堆上创建或销毁。同时,我们在公有区域声明了一个删除的 operator new 和 operator delete 函数,这些函数用于在堆上分配内存和释放内存。
- 由于默认情况下类的 operator new 和 operator delete 函数都是 public 的,因此我们要重新定义它们。而将其声明为 delete,则完全禁止直接在堆上分配和释放内存,从而避免对象在堆上创建。
使用该类时,只能通过栈上的对象进行操作:
StackOnly obj;
【注意】
- 确保了
StackOnly类的对象只能在栈上创建。但是这种方法需要注意的是,在某些情况下,可能会因为需要使用 operator new 和 operator delete 函数而无法编译通过,因此使用时需要慎重考虑。
(四)设计一个不能被继承的类
实现方法:
- 在使用 C++98 标准时,可以通过将构造函数和析构函数设为私有,并且不提供公共的静态工厂方法来实现一个不能被继承的类。
【C++98】 :
- 代码如下:
class NonInherit
{
private:NonInherit() {}~NonInherit() {}public:// 禁止通过静态工厂方法创建对象static NonInherit* GetInstance() {return NULL;}
};
实现方法:
- 在C++11以及后续的标准中,你可以在类声明的末尾添加关键字
final,来显式地指示该类是不可被继承的。
【C++11】:
- 代码如下:
class A final
{// ....
};
【解释说明】
- 在上述示例中,我们在类的定义前使用了 final关键字,将 A声明为最终类。这意味着其他类无法从A继承。
- 如果其他类尝试继承 A,编译器将会报错。
例如:
class A final
{//...
};class B : public A { // 编译错误// 类定义
};
报错如下:

总结
这些特殊类的设计目的是根据特定的需求和编程场景来确定的;
它们有助于代码的组织、可维护性、可扩展性和重用性。通过合理地设计和使用这些特殊类,可以提高代码的质量、可读性和可靠性。
到此,关于本期特殊类设计便讲解结束了。感谢大家的观看和支持!!!

相关文章:
【C++】—— 特殊类设计
目录 序言 (一)设计一个不能被拷贝的类 (二)设计一个只能在堆上创建对象的类 (三)设计一个只能在栈上创建对象的类 (四)设计一个不能被继承的类 总结 序言 特殊类设计是指在面…...
MFC删除Button控件具体操作
删除Button按键;删除xxxDlg.h中消息映射函数定义 class Ctest4Dlg : public CDialogEx {... public://afx_msg void OnBnClickedButton1();... }删除xxxDlg.cpp中“DoDataExchange”和“BEGIN_MESSAGE_MAP”中的相关代码 void CtestDlg::DoDataExchange(CDataExch…...
vue、js实现页面全屏
浏览器可能是处于安全的考虑,无法实现进入页面自动全屏,只能通过用户操作的形式触发全屏!!! ps:可以通过登录按钮触发登录成功自动全屏 实测vue中可通过登录点击事件加载组件自动全屏 import { useFulls…...
从零开始探索C语言(四)----循环
文章目录 1. C 循环1.1 while 循环1.2 for 循环1.3 do...1.4 嵌套循环 2. 循环控制语句2.1 break 语句2.2 continue 语句2.3 goto 语句 1. C 循环 有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语…...
JS 替换 JSON 数组中的指定字段名
直接链式操作 JSON.parse(JSON.stringify(你的json数组).replace(/原来的字段名/g, "想要的字段名")); 例如: const list [{ id: "1", area: "南明区" }, { id: "2", area: "云岩区" }]; console.log(JSON.pa…...
WebSocket消息推送
创建WebSocket工具类 package org.jmis.riskassess.config;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.serve…...
二维码智慧门牌管理系统:让城市管理更智能、便捷
文章目录 前言一、二维码智慧门牌管理系统的特点二、数据集约化与规范化三、管理智能化与长效化四、标识规范化与易维护五、服务多元化与便捷化 前言 随着城市化进程的加速,城市管理面临着越来越多的挑战。为了解决地名地址管理交织错综、地名地址支撑政府管理成效…...
React动态添加标签组件
背景 在前端开发的过程中,一些表单的输入经常需要输入多个内容,如果采用一个输入框逗号分隔的方式,展示起来不是很清晰,一般需要采用标签的方式 需求 可以指定空状态时的标题设置标签颜色每个标签的最大长度(字符数)接口传递的时候的分隔标记(是用逗号,还是其他)直接处理表单,不…...
[Linux]套接字通信
摘于https://subingwen.cn,作者:苏丙榅 侵删 文章目录 1. 套接字-socket1.1 概念1.2 网络协议1.3 socket编程1.3.1 字节序1.3.2 IP地址转换1.3.3 sockaddr 数据结构1.3.4 套接字函数 1.4 TCP通信流程1.4.1 服务器端通信流程1.4.2 客户端的通信流程 1.5 扩展阅读1.5.1 初始化套…...
MySQL的故事——MySQL架构与历史
MySQL架构与历史 文章目录 MySQL架构与历史一、MySQL逻辑架构二、并发控制三、事务四、多版本并发控制(MVCC) 一、MySQL逻辑架构 第一层:连接处理、授权认证、安全等等 第二层:查询解析、分析、优化、缓存以及所有的内置函数。包含跨存储引擎的功能&…...
手写Mybatis:第12章-完善ORM框架,增删改查操作
文章目录 一、目标:完善增删改查二、设计:完善增删改查三、实现:完善增删改查3.1 工程结构3.2 完善增删改查类图3.3 扩展解析元素3.4 新增执行方法3.4.1 执行器接口添加update3.4.2 执行器抽象基类3.4.3 简单执行器 3.5 语句处理器实现3.5.1 …...
【1】DDR---容量计算
1、容量计算 density:芯片容量,bit为单位 depth:地址空间, width:数据位宽 densitydepth*width 2、三星DDR 4Gbit(总容量)256M(地址空间)*16(位宽ÿ…...
YashanDB:潜心实干,数据库核心技术突破没有捷径可走
都说数据库是三大基础软件中的一块硬骨头,技术门槛高、研发周期长、工程要求高,市场长期被几大巨头所把持。 因此,实现突破一直是中国数据库产业的夙愿。自上个世纪80年代起,中国数据库产业走过艰辛坎坷的四十余载,终…...
Talk | ICCV‘23南洋理工大学博士后李祥泰:面向统一高效的视频分割方法设计
本期为TechBeat人工智能社区第528期线上Talk! 北京时间9月6日(周三)20:00,南洋理工大学博士后研究员—李祥泰的Talk已准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “面向统一高效的视频分割方法设计”,他分享了其在视…...
怎样把英语视频字幕翻译成中文
我们知道,随着中外文化交流日益频繁,视频翻译作为一种重要的跨文化交流方式,也越来越受到重视。那么,怎样把英语视频翻译成中文,北京视频翻译哪里比较专业? 据了解,视频翻译是直接将一种语言的音…...
智慧铁路:机车整备场数字孪生
机车整备场是铁路运输系统中的重要组成部分,它承担着机车的维修、保养和整备工作,对保障铁路运输的运维和安全起着至关重要的作用。 随着铁路运输的发展、机车技术的不断进步,以及数字化转型的不断推进,数字孪生技术在机车整备场…...
ImageSharp.Web实战:轻松搭建高效图片服务
很多情况下,在开发如PC、H5、小程序等综合平台的时候,图片的展示是个比较头疼的问题。尤其是有会员功能,会员可以上传图片的平台,更是一件麻烦事。平台展示图片的地方,尺寸是定义好的。但用户不配合,上传的…...
端口扫描-安全体系-网络安全技术和协议
端口扫描-安全体系-网络安全技术和协议 端口扫描信息安全的保证体系和评估方法网络安全技术网络攻击和威胁(重要)网络安全协议 端口扫描 全TCP连接:三次握手 半打开式扫描:前两次握手 FIN扫描:不用建立TCP连接 第三方扫描: 拒绝服务攻击有: 同步包风暴ICMP攻击SNMP攻击 都是修改…...
C# wpf 实现截屏框热键截屏功能
wpf截屏系列 第一章 使用GDI实现截屏 第二章 使用DockPanel制作截屏框 第三章 实现截屏框热键截屏(本章) 第四章 实现截屏框实时截屏 第五章 使用ffmpeg命令行实现录屏 文章目录 wpf截屏系列前言一、实现步骤1、响应热键2、截屏显示(1&#…...
springboot + activiti实现activiti微服务化
概述 本文介绍如何将springbootactiviti进行整合,并配合eureka,zuul和feign实现activiti的微服务化,将流程控制和业务逻辑分离. 并实现了几个比较特殊的功能,比如时间段委托(某人请假或出差,出差时间内,所有待办交给被委托人处理),比如节点的无限级加签功能(流程本身有不确定性…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
