当前位置: 首页 > news >正文

【C++】 特殊类设计:从构思到实现,引领设计新潮流

🌈 个人主页:Zfox_
🔥 系列专栏:C++从入门到精通

目录

  • 🚀 前言
  • 一: 🔥 不能被拷贝的类
  • 二: 🔥 只能在堆上创建对象的类
  • 三: 🔥 只能在栈上创建对象的类
  • 四: 🔥 不能被继承的类
  • 五: 🔥 设计一个类,只能创建一个对象(单例模式)
  • 六: 🔥 共勉

🚀 前言

💢 在C++中,类的设计往往需要考虑到特定的使用场景和需求。为了满足这些需求,有时我们需要设计一些具备特殊性质的类,例如不能被拷贝的类、只能在堆上或栈上创建对象的类、不能被继承的类,或者是只能创建一个对象的类(单例模式)。本文将探讨如何通过C++语言的特性和不同版本的标准来实现这些特殊的类设计。

一: 🔥 不能被拷贝的类

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

  • 在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;//...
}

二: 🔥 只能在堆上创建对象的类

💢 实现方式:

  • 1. 将类的构造函数私有拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  • 2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly;}
private:HeapOnly() {}// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&)// or// C++11HeapOnly(const HeapOnly&) = delete;
}
  • 🥝 说明:

    1. 通过将构造函数声明为私有,我们可以防止在类外部构造对象,不管是在栈区、堆区还是静态区;但是我们的目的是要能够在堆上创建对象,所以我们需要 单独提供一个 CreateObj 成员函数,由于在类内部那么就可以调用构造函数来创建一个堆上的对象并返回指向它的指针
    1. 但是 CreateObj 函数必须是静态的,因为如果是普通成员函数,则其第一个参数是 隐藏的 this 指针所以想要调用这个函数来创建对象就必须先有一个对象,然而在构造私有的情况下我们是不可能在类外通过其他方式创建出对象的,这就好比先有鸡还是先有蛋的问题;但 静态成员函数没有 this 指针,所以可以通过类名 + 域作用限定符 的方式进行调用,而不需要通过通过对象调用。
    1. 最后,我们需要删除拷贝构造函数,防止在类外通过下面这种取巧的方式来创建栈区或静态区的对象:

在这里插入图片描述

三: 🔥 只能在栈上创建对象的类

class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj = StackOnly::CreateObj();// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly():_a(0){}private:int _a;
}
  • 🥝 说明:

💢 在类中禁用 operator new 和 operator delete 函数

  • newdelete 是 C++ 中的关键字,其底层通过调用 operator newoperator delete 函数来开辟与释放空间;如果类中没有重载 operator newoperator delete 函数,那么 newdelete 会去调用全局的 operator newoperator delete 函数,特别注意,这两个函数是普通的全局函数,而不是运算符重载,只是它们的函数名是这样。

  • 所以,我们可以在类中重载 operator newoperator delete 函数,然后将它们声明为删除函数,这样就不能通过 newdelete 在堆上创建与销毁对象了;但是这样有一个缺陷,我们只是禁止了在堆上创建对象,但是我们仍然可以在静态区创建对象,与类的要求不符,所以还需要下面一个步骤。

💢 构造私有,提供一个在栈上创建对象的静态成员函数

  • 这种设计方式和设计一个只能在堆上创建对象的类的思路一样,但是注意不能删除拷贝构造函数,否则就不能通过下面这种方式来构造栈对象了 StackOnly st = StackOnly::CreateObj()

  • 但是,不禁用拷贝构造又会导致可以通过拷贝构造创建出静态区上的对象;所以我们设计出的只能在栈上创建对象的类是有缺陷的。

四: 🔥 不能被继承的类

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

五: 🔥 设计一个类,只能创建一个对象(单例模式)

  • 设计模式

💢 设计模式(Design Pattern)是一套被反复使用的、多数人知晓的、经过分类的代码设计经验的总结设计模式的产生过程类似于兵法的产生过程 – 在夏商周时代,由于打仗比较少,所以每次打仗基本都是单纯的对砍,人多就能获胜;但是随着周朝分封制的推行以及周王朝的衰落,各诸侯国进入春秋战国时代,经常互相征战,仗大多了就发现打仗也是要动脑子的,有许多的套路,于是有人就总结出了《孙子兵法》。设计模式也是如此,代码写的多了自然也就有人去总结一些固定的套路。

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

单例模式:

💢 我们之前其实已经接触过一些设计模式了,比如迭代器模式适配器/配接器模式,而 只能创建一个对象的类被称为单例模式。单例模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现方式:饿汉模式懒汉模式

  • 饿汉模式
    就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:static Singleton* GetInstance(){return &m_instance;}
private:// 构造函数私有Singleton() {};// C++98 防拷贝Singleton(Singleton const&);Singleton& operator=(Singleton const&);// or// C++11Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;static Singleton m_instance;
}Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化

🥝 1. 如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

🥝 2. 由于饿汉模式的对象在 main 函数前就被创建,所以它不存在线程安全问题,但是它也存在一些缺点:

  1. 有的单例对象构造十分耗时或者需要占用很多资源,比如加载插件、 初始化网络连接、读取文件等等,会导致程序启动时加载速度慢。

  2. 饿汉模式在程序启动时就创建了单例对象,所以即使在程序运行期间并没有用到该对象,它也会一直存在于内存中,浪费了一定的系统资源

  3. 当多个单例类存在初始化依赖关系时,饿汉模式无法控制。比如A、B两个单例类存在于不同的文件中,我们要求先初始化A,再初始化B,但是A、B谁先启动初始化是由OS自动进行调度控制的,我们无法进行控制。

  • 这些情况使用 懒汉模式(延迟加载)更好。

  • 懒汉模式

// 懒汉
// 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
// 缺点:复杂
class Singleton {
public:static Singleton& GetInstance(){//第一次进入时创建类对象,以后进入直接返回类对象if (_psins == nullptr){_psins = new Singleton;}return *_psins;}//功能示例函数void func(){//对类中的成员变量进行增删查改或进行其他操作}Singleton(const Singleton& sin) = delete;Singleton& operator=(const Singleton& sin) = delete;private:Singleton() {}private:static Singleton* _psins;  //静态单例对象指针的声明private://类的其他成员变量 -- 此类要管理的数据
};
Singleton* Singleton::_psins = nullptr;  //单例对象指针的定义
}
  • 由于懒汉模式是在第一次使用单例对象时才去创建单例对象,所以就不存在程序启动加载慢以及不使用对象浪费系统资源的问题了,同时,我们也可以通过在程序中先使用A对象再使用B对象的方式来控制有初始化依赖关系的单例对象的实例化顺序。

🥝 最后需要说明的是,在实际开发中,单例模式的应用场景非常广泛,但是绝大多数情况下我们都是使用饿汉模式,只有在极少数的特殊场景下才会使用懒汉模式。

六: 🔥 共勉

以上就是我对 【C++】 特殊类设计 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

相关文章:

【C++】 特殊类设计:从构思到实现,引领设计新潮流

🌈 个人主页:Zfox_ 🔥 系列专栏:C从入门到精通 目录 🚀 前言 一: 🔥 不能被拷贝的类 二: 🔥 只能在堆上创建对象的类 三: 🔥 只能在栈上创建对象的…...

性能调优 18. Tomcat整体架构及其设计精髓分析

1. Tomcat介绍 1.1. 介绍 ‌‌‌  这边使用的是Tomcat9来做说明,本章节先对Tomcat架构和设计有个整体认识。后续章节会对Tomcat性能调优做说明。 ‌‌‌  官方文档介绍 ‌‌‌  https://tomcat.apache.org/tomcat-9.0-doc/index.html1.2. Tomcat概念 ‌‌…...

【C++高阶】:特殊类设计和四种类型转换

✨ 人生如梦,朝露夕花,宛若泡影 🌏 📃个人主页:island1314 🔥个人专栏:C学习 ⛺️ 欢迎关注:👍点赞 👂&am…...

kafka基础概念二

1.Kafka中主题和分区的概念 1.主题Topic 主题-topic在kafka中是一个逻辑的概念,kafka通过topic将消息进行分类。不同的topic会被订阅该topic的消费者消费 但是有一个问题,如果说这个topic中的消息非常非常多,多到需要几T来存,因…...

牛客-热身小游戏

题目链接:热身小游戏 第一种写法:线段树 介绍第二种写法:并查集 对于一些已经查询过的点,我们可以往后跳,进行路径压缩,他们的父亲为下一个点。 a数组记录[ l , r ] 之间的乘积,初始值为1。…...

Python 深度学习调试问题

Python–深度学习解决的常见问题 1.在自己写测试样例的时候,有时候可能将要传入的是input_size,不小心传入为input_dim,这个时候会导致出现问题,自定义的卷积模块或者池化等模块会提示类型问题。 解决的策略是: 1.进行assert i…...

linux恶意请求

nginx访问日志: 162.243.135.29 - - [05/Jan/2024:00:12:07 0800] "GET /autodiscover/autodiscover.json?zdi/Powershell HTTP/1.1" 404 153 "-" "Mozilla/5.0 zgrab/0.x"107.151.182.54 - - [04/Mar/2024:11:30:06 0800] "G…...

Java 反射笔记总结(油管)

Java系列文章目录 IDEA使用指南 Java泛型总结(快速上手详解) Java Lambda表达式总结(快速上手详解) Java Optional容器总结(快速上手图解) Java 自定义注解笔记总结(油管) Jav…...

HTML表格、表单、标签、CSS、选择器

目录 一、HTML表格 二、表单 三、布局标签 四、CSS 五、选择器 一、HTML表格 table:表格 tr:行 td:单元格;rowspan:纵向合并相邻单元格;clospan:横向合并相邻单元格 th:单元格加粗居中 border&…...

【javaWeb技术】·外卖点餐小程序(脚手架学习1·数据库)

🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀系统学javaWeb开发_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 【免费】项…...

LVS 实现四层负载均衡项目实战--DR模式

一、环境准备 主机名IP地址router eth0:172.25.254.100 eth1:192.168.0.100 clienteth0:172.25.254.200lvseth1:192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…...

Python与Qt的对应版本

Python与Qt的对应版本并没有严格的一一对应关系,但通常在使用Python与Qt进行开发时,会选择一个兼容性较好的版本组合。Qt的Python绑定库主要是PyQt和PySide,以下是几个常见的搭配: 1. **PyQt5**: - Python 3.5及以上版…...

WPF篇(12)-Image图像控件+GroupBox标题容器控件

Image图像控件 Image也算是独门独户的控件,因为它是直接继承于FrameworkElement基类。 Image控件就是图像显示控件。Image类能够加载显示的图片格式有.bmp、.gif、.ico、.jpg、.png、.wdp 和 .tiff。要注意的是,加载.gif动画图片时,仅显示第…...

LeetCode 热题 HOT 100 (024/100)【宇宙最简单版】

【哈希表】No. 0128 最长连续序列【中等】👉力扣对应题目指路 希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&am…...

如何在AWS上进行 环境迁移

在 AWS 上进行环境迁移通常包括以下几个步骤和最佳实践: 1. 评估和规划 评估当前环境:审查现有的应用程序、数据库、网络架构和依赖关系。确定迁移目标:明确迁移的目标(如成本节约、性能提升、可扩展性等)。选择迁移策略:根据应用程序的类型和复杂性,选择合适的迁移策略…...

云服务器和物理服务器的优缺点对比

云服务器优点在于灵活性强、成本效益高、易于扩展且支持全球化部署;缺点则包括安全性与可控性相对较弱,性能可能受限,以及存在服务中断风险。物理服务器则以其高性能、高稳定性、强安全性和完全可控性著称,但成本较高、扩展性受限…...

postgreSQL16添加审计功能

下载审计插件 https://github.com/pgaudit/pgaudit/releases他的分支版本支持不同的PGSQL按需下载 编译安装审计插件 tar -xvf pgaudit-16.0.tar.gzmake install USE_PGXS1 PG_CONFIG/app/postgresql/bin/pg_config启用postgreSQL审计功能 修改配置文件# 启用 pgAudit shar…...

如何应用OceanBase 的实时SQL诊断,解决AP场景下的痛点

随着数据量的快速增长与用户需求的变化,数据库的管理与优化工作日益凸显其重要性。作为DBA及开发者,您是否曾面临以下挑战: ○ 分析场景下,在处理大规模数据的且耗时较长的查询是,常涉及海量数据的处理及复杂的计算&…...

【数据结构】—— 栈

一、栈的基本概念1、栈的定义2、栈的常见基本操作 二、栈的顺序存储1、栈的顺序存储结构2、顺序栈存储实现(1)初始化(2)判空(3)进栈(4)出栈(5)取栈顶元素&…...

Kafka服务端日志详解

文章目录 服务端日志Topic消息存储方式主体介绍log文件追加记录消息index和timeindex索引文件 日志文件清理Kafka的文件高效读写机制Kafka的文件结构顺序写磁盘零拷贝 合理配置刷盘频率客户端消费进度管理 服务端日志 Kafka的日志信息是通过conf/server.properties文件中的log…...

C++ 数据语义学——进程内存空间布局

进程内存空间布局 1. 栈(堆栈/栈区)2. 堆(堆区)3. BSS段4. 数据段5. 代码段进程内存空间布局示意图可执行文件的内存布局示例代码 当把一个可执行文件加载到内存后,就变成了一个进程。这个虚拟空间(内存&am…...

【数据结构】六、图:2.邻接矩阵、邻接表(有向图、无向图、带权图)

二、存储结构 文章目录 二、存储结构❗1.邻接矩阵1.1无向图❗邻接矩阵-无向图代码-C 1.2有向图❗邻接矩阵-有向图代码-C 1.3带权图1.4性能分析1.5相乘 ❗2.邻接表2.1无向图2.2有向图❗邻接表-C 邻接矩阵VS邻接表邻接矩阵邻接表 ❗1.邻接矩阵 图的邻接矩阵(Adjacency Matrix) 存…...

财务会计与管理会计(三)

文章目录 销售回款提成表MATCH函数的模糊查询在提成类业务中的应用 营业收入分类数据分析OFFSET函数在制作图表数据中的应用 自动生成销售记录对账单VLOOKUP函数的应用 销售回款提成表 MATCH函数的模糊查询在提成类业务中的应用 G3INDEX(I$1:M$1,MATCH(E3,H3:M3,1)) G3INDEX(…...

【数据结构和算法】(基础篇三)——栈和队列

栈和队列 栈(Stack)和队列(Queue)是两种非常基本的数据结构,它们主要用于存储和检索元素。尽管它们都用于管理一组数据项,但它们的访问规则和数组都是不同的。 栈 栈是一种后进先出(Last In,…...

Linux截图工具gsnap移植arm平台过程记录

Linux截图工具gsnap移植arm平台过程记录 最近工作中一款新产品开发接近尾声,需要写文档截图产品图形,找了一款开源的Linux截屏工具gsnap,将其移植到ARM产品中,这里记录一下移植过程。 gsnap 这个工具源代码就是一个C语言源文件&a…...

密码学知识点02

#来自ウルトラマンレオ(雷欧) 1 常见加密方式 2 对称加密 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。 常见加密算法: DES : Data…...

实现Pytest测试用例按顺序循环执行多次

要实现测试用例按顺序循环执行多次,可以使用 pytest 的自定义装饰器或插件。这里有两种方法可以实现这个需求: 方法一:使用 pytest-repeat 插件 pytest-repeat 插件允许你重复执行测试用例。你可以使用 --count 参数来指定每个测试用例的执…...

SVN工作原理和使用示例

SVN(Subversion)是另一种版本控制系统,用于管理项目文件及其变更历史。与Git不同,SVN是集中式版本控制系统,这意味着所有版本控制操作都集中在一个中央服务器上。以下是SVN的工作原理和基本使用示例。 目录 SVN 工作…...

云服务器部署Java+Vue前后端分离项目

1、申请一个云服务器 选择云服务器:阿里云、腾讯云、百度云、京东云、华为云等等,我使用的是阿里云服务器。 2、远程链接服务器 使用FinalShell工具或者其他远程工具,使用SSH链接,主机地址要填写阿里云服务的公网ip,如…...

C++的7种设计模式原则

一、设计模式前言 设计模式(Design Patterns)的“模式”指的是一种在软件设计中经过验证的、解决特定问题的方案。它们不是具体的代码,而是解决常见设计问题的抽象方案或模板。设计模式提供了一种标准的方式来组织代码,以提高代码…...