【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++】 特殊类设计 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp使用echarts在H5上显示报错问题的解决方法

前言 在做uniapp vue3开发的echarts图表的时候,发现在浏览器上面正常运行,但在微信开发者工具上显示报错了,报错如下 原因:在微信小程序中,使用document.getElementById会报错,因为小程序的运行环境是基于…

目前最强的文生图模型?!FLUX完全解读!附体验地址

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,专注于分享AI全维度知识,包括但不限于AI科普,AI工…

css rem之2024

话题开始前 我们都知道1rem是等于html fontSize标签的字体大小的,我们主要用来做移动端网页设计稿等比例在手机上面的显示。 看到的问题 这个html fontsize的大小是通过js动态计算的,而这个js的运行时晚于html渲染的,所以会导致一个问题&am…

python网络爬虫使用代理

Python网络爬虫使用代理的实用指南 在网络爬虫的开发过程中,使用代理是一个非常重要的环节。代理不仅可以帮助爬虫绕过反爬虫机制,还能保护开发者的隐私。本文将介绍如何在Python中使用代理进行网络爬虫,包括基本的设置和示例代码。 1. 代理…

WordPress多用途电子商务博客新闻主题betheme 21.5.6版本

简介: WordPress多用途电子商务博客新闻主题betheme 21.5.6版本 自带500多套模板 BeTheme第一次发布于2014年5月21日,自那时以来,已有数以百万计的人下载了BeTheme,其评分为4.8。 这个主题是WooCommerce支持的,在此…

Git代码管理规范

1. 简介 git 分支分为集成分支、功能分支和修复分支,分别命名为 develop、feature 和 hotfix,均为单数。不可使用 features、future、hotfixes、hotfixs 等错误名称。 master(主分支,永远是可用的稳定版本,不能直接在…

mybatis xml 动态sql相关语法

<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace"com.xiaKangan.mapper.EmpMapper&qu…

JavaScript 文档元素获取

目录 通过id获取文档元素 任务描述 相关知识 什么是DOM 文档元素 节点树 通过id获取文档元素 编程要求 通过类名获取文档元素 任务描述 相关知识 通过类名获取文档元素 编程要求 通过标签名获取文档元素 任务描述 相关知识 通过标签的名字获取文档元素 获取标…

android13 关闭selinux 临时关闭或者永久关闭

总纲 android13 rom 开发总纲说明 目录 1.前言 2.情况分析 2.1 临时关闭 2.2 永久关闭 3.修改方法 3.1 临时修改 3.2 永久关闭 4.编译测试 5.彩蛋 1.前言 在Android操作系统中,SELinux(Security-Enhanced Linux)是一种安全模块,用于提供强制访问控制(MAC)安全…

IDEA自定义注释模版

1.类&#xff08;接口/枚举等同理&#xff09; 2.方法模版 先自定义一个模版组&#xff0c;然后在里面添加模版名&#xff0c;触发快捷键&#xff08;Tab/Enter&#xff09;&#xff0c;模版描述&#xff0c;哪些语言中应用 模版中的自定义参数params和returns可以自动展开参数…

Linux学习记录(五)-------三类读写函数

文章目录 三种读写函数1.行缓存2.无缓存3.全缓存4.fgets和fputs5.gets和puts 三种读写函数 1.行缓存 遇到新行&#xff08;\n&#xff09;,或者写满缓存时&#xff0c;即调用系统函数 读&#xff1a;fgets,gets,printf,fprintf,sprintf写&#xff1a;fputs,puts,scanf 2.无缓…

Golang | Leetcode Golang题解之第328题奇偶链表

题目&#xff1a; 题解&#xff1a; func oddEvenList(head *ListNode) *ListNode {if head nil {return head}evenHead : head.Nextodd : headeven : evenHeadfor even ! nil && even.Next ! nil {odd.Next even.Nextodd odd.Nexteven.Next odd.Nexteven even.N…

贷齐乐系统sql注入漏洞

目录 源码 代码流程 payload编写 全局污染 php小特性 注入思路 payload构造 获取数据库名&#xff0c;这里是不可以使用database的因为括号被过滤乐 在information中查询数据库名 然后获取表名 获取数据 源码 <?php header("Content-type: text/html; char…

基于ssm+vue+uniapp的网上商城小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

mac要装虚拟机吗

在Mac上安装虚拟机可以带来多种好处&#xff0c;‌但同时也存在一些潜在的影响。‌ 首先&#xff0c;‌虚拟机技术允许在同一设备上运行多个操作系统&#xff0c;‌这对于需要测试不同操作系统兼容性的开发者和IT专业人员来说非常有用。‌此外&#xff0c;‌虚拟机还能解决软件…

fastadmin自定义弹框,以及回调函数等问题,

效果图 1&#xff0c; 代码 弹框中使用弹框 弹框1代码主要代码 <div class"form-group ccol-xs-12 col-sm-2" style"text-align: right"><a href"#" data-url"{:url(user/add)}" class"btn btn-info" id"a…

【多线程】如何实现三个线程循环打印ABC

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 如何实现三个线程顺序打印ABC1.1 详细要求描述1.2 使用join()1.3 实现代码1.4 运行结果 2. 如何实现三个线…

小白零基础学数学建模系列-Day6-微分方程模型基础与案例

文章目录 1 微分方程模型概述1.1 微分方程的基本概念和分类1.2 微分方程在科学计算中的重要性1.3 使用Python进行微分方程建模的优势 2 Python在微分方程建模中的应用2.1 常用Python库介绍2.2 Python的符号计算与解析解2.3 Python的数值计算与数值解 3 解微分方程的基本方法与P…

【大学物理】第7章 机械波,平面简谐波的方程,能流密度,惠更斯原理,波的叠加原理,波的干涉,驻波,多普勒原理(清华大学)

目录 7-1 波的基本概念 一、机械波产生的条件 二、横波和纵波 三、波线和波面 四、简谐波 五、物体的弹性形变* 1. 长变 2. 切变 3.容变 六、描述波动的几个物理量 1.波速 u 2.波动周期和频率 3.波长入 7.2 平面简谐波的方程 一、平面简谐波的波动方程 1.一…

即时通讯系统选型:如何为企业选择最佳的私有化即时通讯工具

在企业内部沟通和协作中&#xff0c;选择适合的即时通讯工具对于保障数据安全和提高工作效率至关重要。随着私有化即时通讯工具的出现&#xff0c;企业可以更好地掌握自身数据和系统&#xff0c;并提供更高的安全性和定制化能力。本文将为您提供一些指导&#xff0c;帮助企业选…