C++中特殊类设计/单例模式

特殊类设计

​ 本篇将会介绍一些特殊类的设计:不能拷贝的类、只能在堆上创建的类、只能在栈上创建的对象、不能被继承的类、只能创建一个对象的类(单例模式)。

文章目录

      • 特殊类设计
        • 不能被拷贝的类
        • 只能在堆上创建对象的类
        • 只能在栈上创建对象的类
        • 不能被继承
        • 单例模式(只可以创建一个对象)
          • 饿汉模式
          • 懒汉模式

不能被拷贝的类

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

C++98写法:

​ 在C++98中还没有delete关键字,所以只能把拷贝构造和赋值运算符重载给放在private域中,这样类外对象就访问不到这两个对象,也就不能进行拷贝。

class CopyBan {
public:// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);// ...
};

C++11之后写法:

​ C++11可以直接使用delete关键字将这两个函数删除掉,如下:

class CopyBan {
public:// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};
只能在堆上创建对象的类

​ 只能在堆上创建对象,也就意味着我们不能在类外直接声明定义,所以我们需要将构造函数私有化(不能禁止,还需要创建),同时还需要将拷贝构造函数禁止掉(或者私有化),因为可能通过拷贝构造在栈上创建对象。

​ 既然不能通过在类外定义,那么我们就需要在类内提供一个静态成员函数用于申请堆上的对象,如下:

class HeapOnly {
public:static HeapOnly* CreateObj() {return new HeapOnly;}// ...~HeapOnly() {// ...delete this;}
private:HeapOnly() {}HeapOnly(const HeapOnly&) {}// HeapOnly(const HeapOnly&) = delete;// ...
};

​ 同时还需要在析构函数中将自己给删除掉。

只能在栈上创建对象的类

​ 既然是只能在栈上创建的对象,那么我们就应该禁掉new和delete,但是new和delete是两个全局的关键字,我们可以在类内将new和delete重载,然后使用delete删除掉,这样对象不会被new出来了,如下:

// 第一种写法
class StackOnly {
public:void* operator new(size_t size) = delete;void operator delete(void* p) = delete;// ...
};// 第二种写法
class StackOnly {
public:static StackOnly CreateObj() {return StackOnly();}void* operator new(size_t size) = delete;void operator delete(void* p) = delete;// ...
private:StackOnly() {}
};
不能被继承

​ 只需要将类的构造函数给私有化,子类调用不到基类的构造函数,就不可以继承基类,如下:

class NonIherit {
public:static NonIherit GetInstance() {return NonIherit();}
private:NonIherit() {}
};// 也可以使用C++11中的关键字final
class NonIherit final {// ...
}
单例模式(只可以创建一个对象)

​ 一个类只能创建一个对象,这就是单例模式。该模式可以保证系统中该类中只有一个实例,并提供一个访问它的全局访问点。该实例被所有程序模块共享。比如在某个服务器程序中,将该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过单例对象来获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

​ 单例模式一共有两种设计模式:

饿汉模式

​ 饿汉模式下的单例对象,在程序启动时就创建(进入main函数前),不管将来是否会使用到该单例对象,都会创建出来。如下:

class ConfigInfo {
public:static ConfigInfo* GetInstance() {return &_sinfo;}// 删除拷贝构造和赋值重载ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;// ...
private:// 构造函数私有化ConfigInfo() {}std::string _ip;uint16_t _port;// ...// 静态变量,在类内声明static ConfigInfo _sinfo;
};// 类外定义
ConfigInfo ConfigInfo::_sinfo;

​ 如上所示,我们先将构造函数私有化,同时删除掉拷贝构造和赋值重载函数,接着在类内声明静态对象,在类外进行定义,这个时候就创建出了全局唯一的一个对象,我们可以使用GetInstance进行该对象访问。

​ 对于饿汉模式的优缺点如下:

优点:

​ 实现较为简单。

缺点:

​ 当单例对象较多的时候,会导致进程启动慢,因为各种单例对象在初始化的时候可能需要加载较多的资源。

​ 同时单例对象之间若存在互相依赖关系,将进一步导致效率降低,因为单例对象初始化的顺序不固定。

懒汉模式

​ 懒汉模式就是只有在需要使用该单例对象的时候才会加载该单例对象的资源,也就是一种延迟加载。实现如下:

class ConfigInfo {
public:// static ConfigInfo* GetInstance() {//     // 这种方式在C++11之前,多线程调用会存在线程安全问题//     // 可能会创建出多个单例对象//     // C++11对该问题进行了特殊处理//     static ConfigInfo info;//     return &info;// }static ConfigInfo* GetInstance() {if (_spinfo == nullptr) {// 判断两次_spinfo是否为nullptr是因为我们只需呀对// _spinfo变量初始化一次,也就是上锁初始化一次// 假若只判断_spinfo是否为nullptr,则每次都要加锁// 较为浪费效率std::unique_lock<std::mutex> lock(_mtx);if (_spinfo == nullptr) _spinfo = new ConfigInfo;}return _spinfo;}// 删除拷贝构造和赋值重载ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;// ...void SetIp(const std::string& ip) {_ip = ip;}std::string GetIp() {return _ip;}
private:// 构造函数私有化ConfigInfo() {}std::string _ip;uint16_t _port;// ...static std::mutex _mtx;static ConfigInfo* _spinfo;
};std::mutex ConfigInfo::_mtx;
ConfigInfo* ConfigInfo::_spinfo = nullptr;

​ 如上所示,实现懒汉单例模式一共存在两种方式:

第一种:

​ 直接在GetInstance函数内定义一个静态的对象,每一次返回即可。这种方式实现得最为简单,不过这种方式在C++11之前会存在线程安全问题。C++11对这样的单例模式进行了特殊处理。

第二种:

​ 定义静态对象指针,以及锁,在类外定义该类和对象指针(两个变量基本没有消耗啥资源),然后在GetInstance函数中判断对象静态指针是否被初始化,没有初始化则new一个对象,已经初始化则直接返回即可。

​ 优缺点:

​ 优点:第一次使用实力对象的时候才创建对象,进程启动无负载。多个单例对象实力启动顺序可以控制。

​ 缺点:实现较为复杂。

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

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

相关文章

云计算复习文档

云计算复习文档 一 云计算概述 名词&#xff1a; 云计算 1.0 &#xff1a; 面向数据中心管理员的IT基础设施资源虚拟化阶段 通过计算虚拟化技术将企业IT应用与底层的基础设施彻底分离、解耦 将多个企业IT应用实例及运行环境复用在相同的物理服务器上&#xff0c;并通过虚…

探索大规模语言模型(LLM)在心理健康护理领域中的应用与潜力

概述 心理健康是公共卫生最重要的领域之一。根据美国国家精神卫生研究所&#xff08;NIMH&#xff09;的数据&#xff0c;到 2021 年&#xff0c;22.8% 的美国成年人将患上某种形式的精神疾病。在全球范围内&#xff0c;精神疾病占非致命性疾病负担的 30%&#xff0c;并被世界…

Tensorflow基本概念

简介&#xff1a;本文从Graph讲到Session&#xff0c;同时讲解了tf.constant创建tensor的用法和variable需要初始化的知识点&#xff0c;可以给你打好一个学习Tensorflow的基础。本文都是基于TensorFlow1.14.0的版本下运行。 本专栏将会系统的讲解TensorFlow在1.14.0版本下的各…

Ubuntu+ROS 机械臂拾取和放置

官方链接&#xff1a;https://github.com/skumra/baxter-pnp 1.下载并安装 SDK 依赖项 sudo apt-get install python-wstool python-rosdep 2.创建新的 catkin 工作区 mkdir -p ~/ros_ws/src cd ~/ros_ws/src 3.使用 wstool 下载 rosinstall 文件并将其复制到 Catkin 工作区…

w~视觉~合集23

我自己的原文哦~ https://blog.51cto.com/whaosoft/12548542 #DragGAN 在 AIGC 的神奇世界里&#xff0c;我们可以在图像上通过「拖曳」的方式&#xff0c;改变并合成自己想要的图像。比如让一头狮子转头并张嘴&#xff1a; 实现这一效果的研究出自华人一作领衔的「Drag You…

从电动汽车到车载充电器:LM317LBDR2G 线性稳压器在汽车中的多场景应用

附上LM317系列选型&#xff1a; LM317BD2TG-TO-263 LM317BTG-TO-220 LM317BD2TR4G-TO-263 LM317D2TG-TO-263 LM317D2TR4G-TO-263 LM317TG-TO-220 LM317LBDR2G-SOP-8 LM317LDR2G-SOP-8 LM317MABDTG-TO-252 LM317MABDTRKG-TO-252 LM317MA…

【已解决】git push一直提示输入用户名及密码、fatal: Could not read from remote repository的问题

问题描述&#xff1a; 在实操中&#xff0c;git push代码到github上一直提示输入用户名及密码&#xff0c;并且跳出的输入框输入用户名和密码后&#xff0c;报错找不到远程仓库 实际解决中&#xff0c;发现我环境有两个问题解决&#xff1a; git push一直提示输入用户名及密码…

测试实项中的偶必现难测bug--互斥逻辑异常

问题: 今天线上出了一个很奇怪的问题,看现象和接口是因为数据问题导致app模块奔溃 初步排查数据恢复后还是出现了数据重复的问题,查看后台实际只有一条数据,但是显示在app却出现了两条一模一样的置顶数据 排查: 1、顺着这个逻辑,我们准备在预发复现这个场景,先是cop…

二五、pxe自动装机

pxe自动装机 pxe------------------------------自动安装系统必要的运行环境 无人值守--------------------为系统定制化的安装需要的软件 pxe的优点&#xff1a; 1、规模化&#xff1a;同时装配多台服务器&#xff08;20-30&#xff09; 2、自动化&#xff1a;系统安装和…

算法魅力-二分查找实战

目录 前言 算法定义 朴素二分模版 二分查找 二分的边界查找 在排序数组中查找元素的第一个和最后一个位置&#xff08;medium&#xff09; 暴力算法 二分查找 边界查找分析 山峰数组的峰顶 暴力枚举 二分查找 搜索旋转排序数组中的最小值&#xff08;medium&#xf…

# 第20章 Cortex-M4-触摸屏

第20章 Cortex-M4-触摸屏 20.1 触摸屏概述 20.1.1 常见的触摸屏分类 电阻式触摸屏、电容式触摸屏、红外式触摸屏、表面声波触摸屏 市场上用的最多的是电阻式触摸屏与电容式触摸屏。红外管式触摸屏多用于投影仪配套设备。 电阻式触摸屏构成&#xff1a;整个屏由均匀电阻构成…

大数据新视界 -- 大数据大厂之 Impala 性能优化:基于数据特征的存储格式选择(上)(19/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Gitcode文件历史记录查看和还原

文件历史记录 文件历史记录用于记录代码文件的更改历史&#xff0c;它允许用户查看文件的不同版本&#xff0c;了解每个版本的修改内容、作者和提交消息。这对于跟踪文件演进、恢复错误更改、审查代码以及了解项目进展都非常有用。 文件历史记录功能提供了以下核心功能&#…

数据结构-二叉树及其遍历

🚀欢迎来到我的【数据结构】专栏🚀 🙋我是小蜗,一名在职牛马。🐒我的博客主页​​​​​​ ➡️ ➡️ 小蜗向前冲的主页🙏🙏欢迎大家的关注,你们的关注是我创作的最大动力🙏🙏🌍前言 本篇文章咱们聊聊数据结构中的树,准确的说因该是只说一说二叉树以及相…

Java集合(Collection+Map)

Java集合&#xff08;CollectionMap&#xff09; 为什么要使用集合&#xff1f;泛型 <>集合框架单列集合CollectionCollection遍历方式List&#xff1a;有序、可重复、有索引ArrayListLinkedListVector&#xff08;已经淘汰&#xff0c;不会再用&#xff09; Set&#xf…

Python学习------第八天

函数 函数的传入参数 掌握函数返回值的作用 掌握函数返回值的定义语法 函数的嵌套调用&#xff1a; 函数的局部变量和全局变量 局部变量的作用&#xff1a;在函数体内部&#xff0c;临时保存数据&#xff0c;即当函数调用完成后&#xff0c;则销毁局部变量。 money 5000000 n…

reduce-scatter:适合分布式计算;Reduce、LayerNorm和Broadcast算子的执行顺序对计算结果的影响,以及它们对资源消耗的影响

目录 Gather Scatter Reduce reduce-scatter:适合分布式计算 Reduce、LayerNorm和Broadcast算子的执行顺序对计算结果的影响,以及它们对资源消耗的影响 计算结果理论正确性 资源消耗方面 Gather 这个也很好理解,就是把多个进程的数据拼凑在一起。 Scatter 不同于Br…

C++- 基于多设计模式下的同步异步日志系统

第一个项目:13万字,带源代码和详细步骤 目录 第一个项目:13万字,带源代码和详细步骤 1. 项目介绍 2. 核心技术 3. 日志系统介绍 3.1 为什么需要⽇志系统 3.2 ⽇志系统技术实现 3.2.1 同步写⽇志 3.2.2 异步写⽇志 4.知识点和单词补充 4.1单词补充 4.2知识点补充…

Node.js GET/POST请求、WEB模块使用介绍 (基础介绍 八)

GET/POST请求 在很多场景中&#xff0c;我们的服务器都需要跟用户的浏览器打交道&#xff0c;如表单提交。 表单提交到服务器一般都使用 GET/POST 请求。 本章节我们将为大家介绍 Node.js GET/POST请求。 获取GET请求内容 由于GET请求直接被嵌入在路径中&#xff0c;URL是…

字节青训-小M的多任务下载器挑战、版本号比较

目录 一、小M的多任务下载器挑战 题目背景 题目内容 数据输入 数据输出 数据与约定 示例1 示例2 解题思路&#xff1a; 问题理解 数据结构选择 算法步骤 最终代码&#xff1a; 运行结果&#xff1a; 二、版本号比较 问题描述 样例 示例 1: 示例 2: 示例 3:…