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

C++进阶 特殊类的设计

本篇博客介绍:介绍几种特殊的类

特殊类的设计

    • 设计一个类不能被拷贝
    • 设计一个类 只能在堆上创建对象
    • 设计一个类 只能在栈上创造对象
    • 设计一个类不能被继承
    • 单例模式
      • 饿汉模式
      • 懒汉模式
      • 单例模式对象的释放问题
    • 总结

设计一个类不能被拷贝

我们的拷贝只会发生在两个场景当中

  • 拷贝构造函数
  • 赋值运算符重载

所以说我们只需要让类失去 或者说不能使用这两个函数即可

这里有两个解决方案

在C++98中

我们将拷贝构造函数只声明不定义 并且将其访问权限设置为私有即可

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

原因如下

  • 我们设置为私有化之后 就能够禁止用户在外面调用 如果不设置私有 用户可以在外面定义并且调用
  • 因为我们不需要这个函数 所以定义没有意义 并且如果定义的话就可以在类内部调用从而以另一个函数完成拷贝

在C++11中

在C++11中 如果我们需要禁用一个函数直接使用delete关键字即可

代码标识如下

class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};

设计一个类 只能在堆上创建对象

这个题目很有意思 让我们只能在堆上创建对象 也就是说我们不能在栈区还有静态区创建对象

再通俗一点说 我们不能创建局部变量和全局变量

用代码标识就是 下面的定义方式不允许

HeapOnly ho;
static HeapOnlu ho;

我们都知道 创建对象是需要构造函数的 所以说我们只需要将构造函数私有化之后 上面的创建方式就不允许了

但是实际上构造函数私有化之后我们也不能定义堆上的对象了

此时我们就需要创建一个新的函数 让这个函数帮助我们创建一个对象来

设计代码如下

  static HeapOnly* CreateObject() {   return new HeapOnly;  }

我们使用一个静态函数new出来一个新的对象 然后返回这个对象的指针 当然我们也只有这种方式可以获取新的对象 也就是说我们只能在堆上创建对象了

与此同时我们要禁用拷贝构造和赋值运算符重载

因为可能会有人写出这样子的代码导致我们的对象创建在栈上

HeapOnly* HO2 = HeapOnly(HO1);

禁用拷贝构造和赋值运算符重载的思路同第一

设计一个类 只能在栈上创造对象

设计思路和只能在堆上创建对象类似 我们都是私有化构造函数之后暴露出一个静态函数来让外部调用

这个静态函数的唯一功能就是在栈上创建一个对象并且返回这个对象

class StackOnly
{
public:static StackOnly Createobj(){return StackOnly();}
private://将构造函数设置为私有StackOnly(){}
};

这里有一点需要特别注意 因为我们是在函数的内部创建了一个对象 所以说这个对象是一个局部变量 所以说我们必须要使用传值返回而不能使用传引用返回

而传值返回不可避免的一点就是 我们需要使用拷贝构造函数

但是呢 有了拷贝构造函数之后我们就没办法限制在堆和静态区创建对象了 因为用户可以通过拷贝构造的方式来实现这一点

所以说我们一定没办法禁止全局对象的创建 即在静态区中创建对象

但是我们还是有办法禁止对象在堆上创建

因为new和delete的底层使用的是operator new和operator delete所以说我们只需要在类中禁用这两个成员函数即可

void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;

设计一个类不能被继承

在C++98中

在C++98中 因为子类必须要调用父类的构造函数 所以说我们只需要将父类的构造函数私有化

此时父类就成为了事实上不能被继承的类了 代码标识如下

class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private://构造函数私有NonInherit(){}
};

在C++11中

在C++11中 提供了一个关键字给我们使用 final

  • final关键字 如果我们在一个类的后面加上final 那么该类就是最终类 不能被继承
class A final
{//...  
};

单例模式

设计模式概念:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

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

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

单例模式有两种实现方式 分别是 饿汉模式懒汉模式

饿汉模式

饿汉模式我们可以理解为这个人十分的饥饿 所以说食物必须要提前准备好

反应到代码中 就是这个单例要在main函数启动前就要准备好

设计思路如下

  • 我们让这个类中有一个静态的类对象 只声明不定义
  • 将构造函数私有化 私有化之后外部就不能通过构造函数来创建对象
  • 删除拷贝构造和赋值运算符重载函数
  • 在类外面定义这个静态对象
  • 暴露出一个静态函数 返回这个静态对象的指针

至此外面的饿汉模式就设计完毕了

//单例模式:全局只有唯一对象
//饿汉模式:一开始就创建对象(main函数之前)
class InfoSingleton
{
public:static InfoSingleton& GetInstance(){return _sins;}
private:InfoSingleton(){}InfoSingleton(InfoSingleton& info) = delete;InfoSingleton& operator=(const InfoSingleton& info) = delete;
private:static InfoSingleton _sins;//声明
};
InfoSingleton InfoSingleton::_sins;//定义,属于类域::,可以调用构造函数

饿汉模式的优缺点

优点:

  • 实现方式简单
  • 没有线程安全相关问题

缺点:

  • 因为在main函数之前就要定义 可能会导致启动慢
  • 无法控制单例初始化顺序

懒汉模式

懒汉模式我们可以理解为这个人十分的懒 所以说不到最后一刻要交任务的时候绝对不会做事的

反应到代码中 就是我们需要使用这个单例的时候这个单例才会创建 也就是说在main函数里面创建

设计思路如下

  • 我们让这个类中有一个静态的类对象 只声明不定义
  • 将构造函数私有化 私有化之后外部就不能通过构造函数来创建对象
  • 删除拷贝构造和赋值运算符重载函数
  • 在类外面定义这个静态对象指针设置为空
  • 暴露出一个静态函数 如果说指针为空则我们构造并返回一个新的对象

至此我们的懒汉模式就设计完毕了

//懒汉模式
class InfoSingleton
{
public:static InfoSingleton& GetInstance(){//第一次获取单例对象的时候创建对象if (_psins == nullptr){//第一次获取单例对象的时候创建对象_psins = new InfoSingleton;}return *_psins;}
private:InfoSingleton(){}InfoSingleton(const InfoSingleton& info) = delete;InfoSingleton& operator=(const InfoSingleton& info) = delete;private:static InfoSingleton* _psins;
};
InfoSingleton* InfoSingleton::_psins=nullptr;

懒汉模式的优缺点

优点:

  • 可以主动控制定义顺序
  • 在main函数后面启动 不影响启动时间

缺点:

  • 有很严重的线程安全问题

如果有两个线程同时进入了GetInstance()函数内部就有可能发生线程安全问题 而导致产生两个或多个对象出来

我们这里推荐一种双检查加锁模式

代码标识如下

static InfoSingleton& GetInstance(){//第一次获取单例对象的时候创建对象if (_psins == nullptr)//对象new出来以后,避免每次都加锁的检查,提高性能{_smtx.lock();if (_psins == nullptr)//保证线程安全的检查且只new一次{_psins = new InfoSingleton;}_smtx.unlock();}return *_psins;}

首先我们检查下 对象指针是否为空 如果为空我们加锁 (主要是为了避免无脑先加锁带来的效率损失)

其次我们加锁 并且再次检查对象指针是否为空 (主要是为了线程安全问题)

如果为空我们创建对象之后解锁 如果不为空我们直接解锁

单例模式对象的释放问题

我们的单例模式对象创建之后一般会运行到程序结束 所以说一般不存在释放问题

如果说就非要释放的话我们可以创造一个函数来释放我们的对象

static void DelInstance()
{_mtx.lock();if (_inst != nullptr){delete _inst;_inst = nullptr;}_mtx.unlock();
}

此外如果我们担心内存泄漏问题的话也可以使用智能指针来管理该对象

总结

在这里插入图片描述

相关文章:

C++进阶 特殊类的设计

本篇博客介绍:介绍几种特殊的类 特殊类的设计 设计一个类不能被拷贝设计一个类 只能在堆上创建对象设计一个类 只能在栈上创造对象设计一个类不能被继承单例模式饿汉模式懒汉模式单例模式对象的释放问题 总结 设计一个类不能被拷贝 我们的拷贝只会发生在两个场景当…...

NLP序列标注问题,样本不均衡怎么解决?

【学而不思则罔,思而不学则殆】 1.问题 NLP序列标注问题,样本不均衡怎么解决? 2.解释 以命名实体识别(NER)为例,这个样本不均衡有两种解释: (1)实体间类别数量不均衡…...

大端和小端

大端和小端 大端(Big Endian)和小端(Little Endian)是两种不同的字节序排列方式,用于解释多字节数据在内存中的存储顺序。 在大端字节序中,高位字节(最高有效位)存储在低位地址&am…...

C++快速回顾(二)

前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》,结合我自己的工作学习经历,我准备写一个音视频系列blog。C/C是音视频必…...

【LVS】1、LVS负载均衡群集

1.群集的含义: Cluster、群集、集群 由多台主机构成并作为一个整体,只提供一个访问入口(域名与IP地址);可伸缩 2.集群使用的场景: 高并发 3.企业群集的分类: 根据群集所针对的目标差异&a…...

el-tree 懒加载树

el-tree 懒加载树 添加自定义图标指定叶子节点懒加载 <template><div><el-treeclass"filter-tree":data"treeData":props"defaultProps"ref"tree"lazy:load"loadTree":expand-on-click-node"true"…...

到江西赣州ibm维修服务器之旅-联想X3850 x6黄灯故障

2023年08月15日&#xff0c;一位江西赣州工厂客户通过朋友介绍与冠峰售前工程师取得联系&#xff0c;双方对产品故障前后原因沟通的大致情况如下&#xff1a; 服务器型号&#xff1a;Lenovo system x3850 x6 为用户公司erp仓库服务器 服务器故障&#xff1a;正常使用过程中业…...

VMware 虚拟机三种网络模式详解

文章目录 前言桥接模式(Bridged)桥接模式特点: 仅主机模式 (Host-only)仅主机模式 (Host-only)特点: NAT网络地址转换模式(NAT)网络地址转换模式(NAT 模式)特点: 前言 很多同学在初次接触虚拟机的时候对 VMware 产品的三种网络模式不是很理解,本文就 VMware 的三种网络模式进行…...

ASP.NET指定变量数据类型,速度提高了100倍

ASP.NET指定变量数据类型&#xff0c;速度提高了100倍由自动编程人工智能 发表在专区 10亿次求余数为0的计算&#xff1a; ASP运行速度130秒左右 ASP.NET Dim i, c, max 如果不指定数据类型&#xff0c;运行要120秒左右 Dim i, c, max As Integer 指定数据类型&#xff0c;运…...

PyArmor 一键加密

使用&#xff1a; pyarmor obfuscate main.py 参考&#xff1a;Python代码加密方案_python加密代码_wgr_1009的博客-CSDN博客 一 简介 PyArmor是用于保护Python代码的工具&#xff0c;它可以将Python脚本编译成加密的字节码&#xff0c;以增加代码的保护性。它的主要目的是防…...

redis--持久化

redis持久化 在 Redis 中&#xff0c;持久化是一种将数据从内存写入到磁盘的机制&#xff0c;以便在服务器重启或崩溃时能够恢复数据。Redis 提供了两种主要的持久化方式&#xff1a;RDB&#xff08;Redis Database Snapshot&#xff09;和AOF&#xff08;Append-Only File&am…...

管理外部表

官方文档地址&#xff1a;Managing Tables 关于外部表 Oracle 数据库允许您对外部表中的数据进行只读访问。外部表定义为不驻留在数据库中的表&#xff0c;通过向数据库提供描述外部表的元数据&#xff0c;数据库能够公开外部表中的数据&#xff0c;就好像它是驻留在常规数据…...

数字图像处理-AWB跳变

1、自动白平衡&#xff08;AWB&#xff09;算法是相机中常用的图像处理技术&#xff0c;它能够自动调整图像中的白平衡&#xff0c;使得图像中的颜色更加真实、自然。然而&#xff0c;在实际应用中&#xff0c;AWB算法也存在着一些问题&#xff0c;例如AWB跳变&#xff08;Whit…...

DNNGP、DeepGS 和 DLGWAS模型构成对比

一、DNNGP DNNGP 是基于深度卷积神经网络&#xff0c;这个结构包括一个输入层&#xff0c;三个卷积层&#xff0c;一个批标准化层&#xff0c;两个dropout层&#xff0c;一个平坦化层&#xff0c;一个 dense层。 dropout层&#xff1a;在神经网络中,dropout层是一个非常有效的正…...

postgresSQL 配置文件设置

postgres.conf 是 PostgreSQL 数据库的主要配置文件&#xff0c;其中包含了许多关于数据库行为的设置。以下是一些常见的配置项&#xff1a; listen_addresses: 这个参数定义了 PostgreSQL 服务监听的网络地址。默认值是 ‘localhost’&#xff0c;这意味着只有本机的客户端才能…...

【bug】Unity无法创建项目

bug UnityHub无法创建项目 UnityHub无法创建项目 出现的问题&#xff1a;在创建新项目时弹出来一个 无法创建项目 尝试的方法&#xff1a; 刷新许可证 ❌没用退出账号重新登陆 ❌没用重启电脑 ❌没用 最后发现是什么问题呢&#xff1f; 2021.3.3这个版本我之前在资源管理器中…...

跨境外贸业务,选择动态IP还是静态IP?

在跨境业务中&#xff0c;代理IP是一个关键工具。它们提供了匿名的盾牌&#xff0c;有助于克服网络服务器针对数据提取设置的限制。无论你是需要经营管理跨境电商店铺、社交平台广告投放&#xff0c;还是独立站SEO优化&#xff0c;代理IP都可以让你的业务程度更加丝滑&#xff…...

Hlang社区-社区导航栏实现

文章目录 前言项目结构导航实现创作中心移动小球消息提示完整代码前言 okey,这里的话是我们社区导航栏的实现: 废话不多说,看看效果: 我甚至为此用New Bing生成了一个Logo。 项目结构 废话不多说,先来看到我们的项目结构: 在这里导航栏是一个组件。 在App.vue里面直…...

Kestrel和ISS服务器下的配置

一、Kestrel服务器 Kestrel是ASP.NET Core框架中的一个跨平台的Web服务器。它是ASP.NET Core应用程序默认的HTTP服务器&#xff0c;并且可作为独立的Web服务器来托管ASP.NET Core应用程序。 Kestrel具有以下特点和功能 1、跨平台 Kestrel是完全跨平台的&#xff0c;可以在Wind…...

uniapp选择只选择月份demo效果(整理)

<template><view style"margin-top: 200rpx;"><!-- mode"multiSelector" 多列选择器 --><view><picker :range"years" :value"echoVal" change"yearChange" mode"multiSelector">{…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...