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

【C++高阶】解锁C++的深层魅力——探索特殊类的奥秘

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:C++ 类型转换
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀C++特殊类

  • 📒1. 不能被拷贝的类
  • 📙2. 只能在堆上创建对象
  • 📚3. 只能在栈上创建对象
  • 📜4. 不能被继承的类
  • 📝5. 只能创建一个对象(单例模式)
    • 🌞设计模式
    • ⭐单例模式
  • 📖6. 总结


前言:在C++这片浩瀚的编程海洋中,基础语法与常用库如同岛屿与浅滩,引领着每一位初学者逐步前行。然而,当我们的航程逐渐深入,便会发现那些隐藏于波涛之下的特殊类,它们如同深海中的宝藏,等待着勇敢的探索者去发掘

特殊类,作为C++语言中的高级特性之一,不仅承载着对面向对象编程思想的深刻体现,更是解决复杂问题、优化程序性能的利器。它们包括但不限于模板类、智能指针、迭代器、多态基类(抽象类)、以及那些利用高级特性(如RAII、类型萃取等)设计的独特类。这些特殊类以其独特的设计理念和强大的功能,在C++的各个领域发挥着不可替代的作用

现在我将带领大家一同踏上这场探索之旅,通过详细解析C++中的特殊类,揭示它们的设计原理、应用场景以及使用技巧。我们将从基础概念出发,逐步深入到高级特性,通过实例演示和代码分析,帮助读者逐步掌握这些特殊类的精髓

让我们一同揭开C++特殊类的神秘面纱,领略其独特的魅力与力量吧!


📒1. 不能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载

实现方式:

因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

  • C++98:将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可

原因:

  • 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
    能禁止拷贝了
  • 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
    反而还简单,而且如果定义了就不会防止成员函数内部拷贝了
  • C++11:C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数

代码示例 (C++):

//不能被拷贝的类
//C++98
class CopyBan
{// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};//C++11
class CopyBan
{// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;//...
};

📙2. 只能在堆上创建对象

实现方式:

将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象
提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建


方案一:析构函数私有化:

正常创建对象,会自动调用析构函数,将析构函数私有化,外部代码将无法直接调用delete来销毁对象,因为这将尝试访问一个不可访问的析构函数,要想销毁对象就得自己定义成员函数来操作

代码示例 (C++):

//只能在堆上创建对象
class HeapOnly
{
public:// 对象的销毁/*static void Destroy(HeapOnly* ptr){delete ptr;}*/// 对象的销毁void Destroy(){delete this;}private:~HeapOnly(){cout << "~HeapOnly()" << endl;}
};int main()
{HeapOnly* ptr = new HeapOnly;//HeapOnly::Destroy(ptr);ptr->Destroy();return 0;
}

方案二:构造函数私有化:

通过将构造函数私有化,我们可以限制对象创建的方式,从而实现只能在堆上创建对象

代码示例 (C++):

class HeapOnly
{
public:// 静态的成员函数,完成完成堆对象的创建static HeapOnly* CreateObj(){return new HeapOnly;}private:HeapOnly(){cout << "HeapOnly()" << endl;}// 防止别人调用拷贝在栈上生成对象//C++98HeapOnly(const HeapOnly& copy);//C++11HeapOnly(const HeapOnly& copy) = delete;
};int main()
{HeapOnly* ptr = HeapOnly::CreateObj();//HeapOnly copy(*ptr);return 0; 
}

📚3. 只能在栈上创建对象

实现方式:

将类的构造函数私有,提供一个静态的成员函数,在该静态成员函数中完成栈对象的创建

思考一下:这里的实现方法和上面似乎有点相同,但是我们在实现时,是否也要像上面一样delete掉拷贝构造呢?

静态成员函数 -> 完成栈对象的创建:

static StackOnly CreateObj(){StackOnly obj;return obj;}
// 这里会让对象在堆上创建
StackOnly* ptr = new StackOnly(obj);
// 如果我们 delete 拷贝构造,会发现CreateObj()也会受到影响,所以我们只能另辟蹊径
// 我们要从 new 下手让我们不能用 new 来创建对象

实现专属的operator new

void* operator new(size_t size)
{cout << "void* operator new(size_t size)" << endl;return malloc(size);
}

当我们new这类对象时,我们会优先调用operator new,而不是全局(就近原则),因此我们delete掉我们自己实现的operator new,我们就不能在使用new创建对象

代码示例 (C++):

// 只能在栈上创建的类
class StackOnly
{
public:static StackOnly CreateObj(){StackOnly obj;return obj;}// 如果我们将拷贝构造delete,那么我们就无法创建对象,因为在CreateObj()中也会发生拷贝构造//StackOnly(const StackOnly& copy) = delete;void* operator new(size_t size) = delete;//实现专属的operator new//当我们new这类对象时,我们会优先调用operator new,而不是全局(就近原则)/*void* operator new(size_t size){cout << "void* operator new(size_t size)" << endl;return malloc(size);}*/private:StackOnly(){cout << "StackOnly()" << endl;}
};int main()
{StackOnly obj = StackOnly::CreateObj();// 发生拷贝构造是会在堆上创建类,但是我们能不能将拷贝构造delete呢?//StackOnly* ptr = new StackOnly(obj);return 0;
}

📜4. 不能被继承的类

实现方式:

  • C++98:中构造函数私有化,派生类中调不到基类的构造函数就无法继承

  • C++11:一个类如果不希望被其他类继承,可以通过将其声明为final类来实现

代码示例 (C++):

// 不能被继承的类
//C++98
class A
{
public:static A GetInstance(){return A();}
private:A(){}
};// 不能被继承的类
//C++11
class A final
{// ....
};

📝5. 只能创建一个对象(单例模式)

🌞设计模式

设计模式(Design Patterns)是一种在软件开发中用于解决常见问题的可重用的解决方案。它们不是代码本身,而是关于如何组织代码和对象之间关系的一种描述和指导,是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结

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

设计模式是面向对象设计原则的体现,它们通过提供可重用的解决方案来帮助开发者更好地设计和实现软件系统。在实际开发中,可以根据具体的需求和场景选择合适的设计模式来解决问题


⭐单例模式

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

单例模式的核心在于控制实例的数目,节省系统资源,并允许全局访问

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

  • 饿汉模式
  • 懒汉模式

  • 饿汉模式
    提前创建好程序,启动时就创建一个唯一的实例对象 -> (提前做好)

优点:实现简单
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定

代码示例 (C++):

class A
{
public:static A* GetInstance(){return &_inst;}void Add(const string& key, const string& value){_dict[key] = value;}void Print(){for (auto& kv : _dict){cout << kv.first << ":" << kv.second << endl;}}private:A() // 私有构造函数  {}//限制类外随意创建对象A(const A& copy) = delete;A& operator=(const A& aa) = delete;map<string, string> dict;static A _inst;// 静态实例  };
// 类的外部定义静态实例  
A A::_inst;int main()
{A::GetInstance()->Add("sort", "排序");A::GetInstance()->Add("left", "左边");A::GetInstance()->Add("right", "右边");A::GetInstance()->Print();return 0;
}
  • 懒汉模式
    第一次用的时候在创建 -> (现吃现做)

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

优点:第一次使用实例对象时,创建对象,进程启动无负载。多个单例实例启动顺序自由控制
缺点:复杂

注意:new的懒汉对象一般不需要释放,进程正常结束会释放资源

代码示例 (C++):

class B
{
public:static B* GetInstance(){// 第一次为空的时候,创建对象if (_inst == nullptr){_inst = new B;}return _inst;}void Add(const string& key, const string& value){_dict[key] = value;}void Print(){for (auto& kv : _dict){cout << kv.first << ":" << kv.second << endl;}}static void DelInstance(){if (_inst){delete _inst;_inst = nullptr;}}
private:B(){}//限制类外随意创建对象B(const B& copy) = delete;B& operator=(const B& bb) = delete;map<string, string> _dict;static B* _inst; // 指针的形式class gc{public:~gc(){DelInstance();}};static gc _gc;
};B* B::_inst = nullptr;
B::gc B::_gc;int main()
{B::GetInstance()->Add("sort", "排序");B::GetInstance()->Add("left", "左边");B::GetInstance()->Add("right", "右边");B::GetInstance()->Print();return 0;
}

懒汉模式并非这么简单,后面还要牵扯到线程安全,线程安全需要额外处理


📖6. 总结

随着我们一同探索了C++中特殊类的广阔天地,相信你已经对这些高级特性有了更深的理解与感悟。特殊类不仅是C++语言复杂性和强大功能的体现,更是编程艺术的结晶,它们以独特的方式解决了传统编程中难以处理的问题,提升了代码的效率、安全性和可维护性

学习特殊类的过程,实际上是一个不断挑战自我、深化编程思维的过程。它要求我们不仅要掌握语法层面的知识,更要理解其背后的设计思想和实现原理。只有这样,我们才能真正驾驭这些特殊类,将它们灵活地应用于实际开发中,解决实际问题

用一句话总结一下就是:“掌握C++特殊类,就是掌握了一种强大的编程工具,它将助你在编程的世界里走得更远、更稳。”
愿你在未来的编程道路上,能够勇往直前、不断突破自我,创造出更多优秀的作品和成果!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

相关文章:

【C++高阶】解锁C++的深层魅力——探索特殊类的奥秘

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C 类型转换 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀C特殊类 &#x1f4d2;1. 不能被拷贝…...

Vue学习记录之三(ref全家桶)

ref、reactive是在 setup() 声明组件内部状态用的&#xff0c; 这些变量通常都要 return 出去&#xff0c;除了供 < template > 或渲染函数渲染视图&#xff0c;也可以作为 props 或 emit 参数 在组件间传递。它们的值变更可触发页面渲染。 ref &#xff1a;是一个函数&…...

第二十六篇——九地篇:九种形势的应对之道

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 地势的维度重新阐述了懂得人心的重要性&#xff0c;道久其归一为为别人。…...

学习记录:js算法(三十七): 搜索二维矩阵

文章目录 搜索二维矩阵我的思路网上思路 总结 搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; ● 每行中的整数从左到右按非严格递增顺序排列。 ● 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&a…...

拥控算法BBR入门1

拥塞控制算法只与本地有关 一个TCP会话使用的拥塞控制算法只与本地有关。 两个TCP系统可以在TCP会话的两端使用不同的拥塞控制算法 Bottleneck Bandwidth and Round-trip time Bottleneck 瓶颈 BBR models the network to send as fast as the available bandwidth and is 2…...

[Python数据可视化]Plotly Express: 地图数据可视化的魅力

在数据分析和可视化的世界中&#xff0c;地图数据可视化是一个强大而直观的工具&#xff0c;它可以帮助我们更好地理解和解释地理数据。Python 的 Plotly Express 库提供了一个简单而强大的方式来创建各种地图。本文将通过一个简单的示例&#xff0c;展示如何使用 Plotly Expre…...

windows C++ 并行编程-PPL 中的取消操作(四)

并行模式库 (PPL) 中取消操作的角色、如何取消并行工作以及如何确定取消并行工作的时间。 运行时使用异常处理实现取消操作。 请勿在代码中捕捉或处理这些异常。 此外&#xff0c;还建议你在任务的函数体中编写异常安全的代码。 例如&#xff0c;可以使用获取资源即初始化 (RA…...

【数据结构】字符串与JSON字符串、JSON字符串及相应数据结构(如对象与数组)之间的相互转换

前言&#xff1a; 下面打印日志用的是FastJSON依赖库中的 Log4j2。依赖&#xff1a; <!-- Alibaba Fastjson --> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.80</version> …...

LeetcodeTop100 刷题总结(一)

LeetCode 热题 100&#xff1a;https://leetcode.cn/studyplan/top-100-liked/ 文章目录 一、哈希1. 两数之和49. 字母异位词分组128. 最长连续序列 二、双指针283. 移动零11. 盛水最多的容器15. 三数之和42. 接雨水&#xff08;待完成&#xff09; 三、滑动窗口3. 无重复字符的…...

Next-ViT: 下一代视觉Transformer,用于现实工业场景中的高效部署

摘要 由于复杂的注意力机制和模型设计&#xff0c;大多数现有的视觉Transformer&#xff08;ViTs&#xff09;在实际的工业部署场景中&#xff0c;如TensorRT和CoreML&#xff0c;无法像卷积神经网络&#xff08;CNNs&#xff09;那样高效运行。这提出了一个明显的挑战&#x…...

C++知识点示例代码助记

C语言设计期末知识点附示例代码。 1. 基础语法 变量和数据类型&#xff1a; int a 10; // 整型 float b 5.25f; // 单精度浮点型 double c 5.25; // 双精度浮点型 char d A; // 字符型 bool e true; // 布尔型 const int PI 3.14; // 常量输入输出&…...

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 垃圾回收算法

文章目录 垃圾回收机制垃圾判断算法引用计数法可达性分析算法虚拟机栈中的引用&#xff08;方法的参数、局部变量等&#xff09;本地方法栈中 JNI 的引用类静态变量运行时常量池中的常量 垃圾收集算法Mark-Sweep&#xff08;标记-清除&#xff09;算法Copying&#xff08;标记-…...

苍穹外卖Day01-2

导入接口文档 yApi接口管理平台http://api.doc.jiyou-tech.com/ 创建项目 导入接口文件 导入结果界面 Swagger 介绍 使用Swagger你只需要按照它的规范去定义接口及接口相关的信息&#xff0c;就可以做到生成接口文档&#xff0c;以及在线接口调试页面。 官网&#xff1a;ht…...

软考中级软件设计师——数据结构与算法基础学习笔记

软考中级软件设计师——数据结构与算法基本概念 什么是数据数据元素、数据项数据结构逻辑结构物理结构&#xff08;存储结构&#xff09; 算法什么是算法五个特性算法效率的度量时间复杂度空间复杂度 什么是数据 数据是信息的载体&#xff0c;是描述客观事物属性的数、字符及所…...

虚幻引擎 | (类恐鬼症)玩家和NPC语音聊天(中)

虚幻引擎 | &#xff08;类恐鬼症&#xff09;玩家和NPC语音聊天-CSDN博客 上篇偏重实现步骤&#xff0c;中篇偏重理解校准和降低延迟&#xff0c;下篇加入上下文背景array和设置口音 TTS通用参数 ————————————————————————————————————…...

整流电路的有源逆变工作状态

目录 1. 逆变的概念 2. 有源逆变的条件 3. 电流电路的概念 4. 产生逆变的条件 5. 三相桥式全控整流电路的有源逆变工作状态 6. 逆变角的概念 7. 逆变失败的原因 8. 最小逆变角的限制 整流电路的有源逆变状态是指通过控制整流器&#xff0c;使其将直流电源的能量反向送回…...

Android 签名、空包签名 、jarsigner、apksigner

jarsigner是JDK提供的针对jar包签名的通用工具, 位于JDK/bin/jarsigner.exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具, 位于Android SDK/build-tools/SDK版本/apksigner.bat jarsigner&#xff1a; jarsigner签名空包执行的命令&#xff1a; jar…...

java基础(小技巧)

文章目录 一、日志输出二、字符串拼接三、日期比较四、常用注解五、Lombok的原理 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、日志输出 之前使用的方式。在要使用的类里面定义日志类&#xff1a; private static Logger logger LoggerFactory…...

Android Studio 安装配置教程(Windows最详细版)

目录 前言 Android Studio 下载 Android Studio 安装 Android Studio 使用 一、创建默认项目&#xff08;Compose&#xff09; 二、创建常规项目 三、使用ViewBinding 四、查看Gradle版本、SDK版本、JDK版本 ① Gradle版本 ② SDK版本 ③ JDK版本 前言 Android开发…...

Cesium绘制可编辑线

Cesium 第一章 绘制可编辑线 Screen-2024-09-17-202059的副本 文章目录 Cesium一、绘制线二、编辑线三、使用 一、绘制线 1、方法 //场景相机控制viewer.scene.screenSpaceCameraController.enableRotate false; //cesium相机控制 绘制和编辑时 禁止转动场景// 鼠标样式修改…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...