目录

  • 1. 不能被拷贝的类
  • 2. 只能在堆上创建的类
  • 3. 只能在栈上创建的类
  • 4. 不能被继承的类
  • 5. 类的设计模式(单例模式)
    • 5.1 饿汉模式设计
    • 5.2 懒汉模式设计

特殊类的概念: 特殊类是一些具有特殊行为、用途,用特殊方法设计而出的类。


1. 不能被拷贝的类

  创建一个类其不能够被拷贝,不能使用其拷贝构造其他对象,不能用其为其他对象赋值,C++库中就存在着这样的类,诸如,istream、ostream、unique_ptr。C++98及之前都采用私有化拷贝构造与赋值重载的方式来达成,C++11后新增了关键字delete的使用方式可以直接禁止拷贝构造与赋值重载的生成,具体实现细节如下:

//不能被拷贝的类
class NoCopy
{
public:NoCopy(int x = 0):_x(x){}NoCopy(const NoCopy&) = delete;NoCopy& operator=(const NoCopy&) = delete;private:int _x;
};

2. 只能在堆上创建的类

  创建出一个只能在堆上的类,这就意味着只能使用new/malloc这样在堆上动态开辟空间的操作。因此,我们不能直接使用构造函数创建出一个对象,具体的设计方式有两种:

  • 方法1:将构造函数设为私有,再专门设置一个对外接口提供在堆上创建对象的方法
//只能在堆上开辟的类
class HeapOnly
{
public:template<class ...Args>static HeapOnly* CreateObj(Args&&... args)//可变模板参数,可以直接匹配类的构造函数{return new HeapOnly(args...);}HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;private:HeapOnly(int x = 0, int y = 0):_x(x),_y(y){}int _x;int _y;
};

  对外返回new出对象的函数必须要设定为静态成员函数,普通成员函数的调用需要this指针即要用这个类的对象来调用。此类的拷贝构造必须设置其禁止生成,否则会出现HeapOnly n2(*pn1);以此种方式拷贝构造出栈上对象的情况。此类的赋值重载最好也设置为禁止生成,默认生成的赋值重载为浅拷贝。

  • 方法2: 禁止析构函数的生成,使得用构造函数创建的对象无法自动销毁,程序结束时产生报错,因此,就只能使用动态开辟new的方式去创建对象(new不会自动调用析构)
class HeapOnly
{
public:HeapOnly(int x = 0, int y = 0):_x(x),_y(y){}//默认生成的拷贝构造//默认生成的赋值重载//手动释放void Destory(){delete this;}private:~HeapOnly(){cout << "~HeapOnly" << endl;}int _x;int _y;
};

  私有化析构设计的只能堆上开辟的类,其对象不能够自动释放自己的资源,但new申请出的资源还是需要释放的。对象的析构函数被私有化,delete时会调用析构,所以,直接delete对象的指针此种方法是不可行的。可以提供一个专门释放资源的成员函数,成员函数本身就可以使用类内私有的方法。但我们手动释放可能会存在遗忘疏忽的可能,因此,此处可以使用只能指针对对象资源进行管理并配合以定值删除器,shared_ptr<HeapOnly> ptr(new HeapOnly, [](HeapOnly* ptr) { ptr->Destroy(); });

3. 只能在栈上创建的类

  设计一个只能在栈上创建的类,也可以说设计一个不能使用new来创建的类。这里可以采取重载声明一个专属的operator new,然后使用delete禁用的方法来实现。当没有重载operator new时,类会默认使用调用全局的operator new,当有了自己的会默认使用自己。而与operator new相对象的operator delete视情况而定是否要禁用。
  同时,此类的构造函数也必须要设置为私有,对外提供一个专门创建对象的静态成员函数。拷贝构造函数不能禁用,返回对象的对外接口为传值返回,在这一过程中需要调用拷贝构造。赋值重载根据使用场景最好也用delete设置为进行生成。

class StackOnly
{
private:template<class ...Args>static StackOnly CreateObj(Args ...args){return StackOnly(args...);}//不能禁用拷贝构造,CreateObj接口本身就是传值返回StackOnly& operator=(const StackOnly&) = delete;//重载一个类专属的operator newvoid* operator new(size_t n) = delete;//返回值为void*private:StackOnly(){}StackOnly(int x, int y):_x(x),_y(y){}int _x;int _y;
};

4. 不能被继承的类

  C++98即之前是采用了私有化构造函数的方式来使得类本身不能够被继承,因为子类继承父类,在构造时需要调用父类的构造函数对父类进行初始化。而C++11后,则添加了关键字final,被此关键字修饰的类代表其为最终类,不可被继承。

//C++98及之前
class A
{
private:A(){}
};//C++11
class B final
{};

5. 类的设计模式(单例模式)

  类存在着多种多样的设计模式,我们上面所了解到的各种特殊类就可以算作此中。但对于类的设计模式并不是官方的标准,而是在工作与日常中被反复使用,使用性很高的一些设计模式。接下来,就来了解其中一种很常见实用的设计模式,单例模式。单例模式的类常用于存储一些一个进程内只需要一份的资源或信息,诸如内存池、配置信息等。

  • 单例模式: 一个进程的全局范围内,只能够去创建一个对象,单例模式存在两种设计方法,两者各有优劣,其中之一被称作饿汉模式,另一种被称为懒汉模式

5.1 饿汉模式设计

  饿汉模式的类创建一个自己类型的静态成员变量,这个变量属于整个类,并不属于某一个对象。(类内无法定义自己类型的成员变量)这个静态成员变量实际上就相当于是全局变量,其存储在静态区,只不过在类内声明,受类域限制。而需要懒汉模式的对象去存储的资源或信息就存储在这个静态成员变量中,全局的静态变量会在main函数运行之前就创建好并初始化
  具体设计时,将此类的构造函数私有化,专门设计一个对外的接口去返回存储资源或信息的静态成员变量。因为此模式下设计的类整个程序的全局范围内只允许有一份资源,所以,此类的构造函数与赋值重载需要使用delete禁止其生成。
  想要修改或访问单例模式对象中的资源信息,就可以在设计时提供相应的成员函数。

//饿汉模式
namespace hunger
{class Singleton{public:static Singleton* GetInstance(){return &_sint;}Singleton(const Singleton&) = delete;Singleton& operator=(Singleton&) = delete;//修改访问内部资源(修改访问场景:增删改)void setVal(int x, int y){_x = x;_y = y;}private:Singleton(int x = 0, int y = 0):_x(x),_y(y){}int _x;int _y;static Singleton _sint;//类内声明};Singleton Singleton::_sint = { 1, 1 };//类外定义
}

懒汉模式的优点:

  • 1. 全局静态成员变量的创建先于main函数之前,没有线程安全的问题

懒汉模式的缺点:

  • 1. 当单例对象数据较多时,构造初始化很慢成本较高,拖慢整个程序启动的速度
  • 2. 多个单例模式有初始化以来关系时,饿汉模式设计的对象无法控制初始化的顺序

5.2 懒汉模式设计

设计方法1:
  懒汉模式的第一种设计模式也是采用全局的静态成员变量的方式去存储资源,但与饿汉模式不同的是,此时的静态成员变量为指针类型。在程序启动时只需要main函数运行前,创建此静态成员变量时,只需要创建一个指针,待到需要调用此对象时,再正式去new出一个对象
  具体设计时,因为懒汉模式同样也是单例模式设计的一种,所以,同样也需要禁止拷贝构造与赋值重载的生成。再将构造函数私有化,提供一个对外获取对象的静态成员函数接口。存储资源的静态成员变量指针应被初始化为nullptr。调用GetInstance接口时,当指针为nullptr时,new出一个新对象,当指针不为nullptr时,返回现有的指针
  除此之外,还需要创建一个专门用来释放静态成员变量资源的函数接口DelInstance,此接口也要设置为静态成员函数,用于手动释放new出的资源。因为手动释放的不确定性,这里提供两种解决方案:

  • 方法1:定义一个私有的内部类(防止外部去访问),在懒汉模式的类中设置一个此内部类类型的静态成员变量。在其析构函数中,调用懒汉模式对象的资源释放接口。静态成员变量的声明周期随进程,当进程结束时,自动析构同时释放资源。
  • 方法2:使用智能指针帮忙管理,在定值删除器中传入相应的资源释放接口
namespace lazy
{class Singleton{public:static Singleton* GetInstance(){if (_psint == nullptr){return new Singleton();}}static void DelInstance(){if (_psint){delete _psint;_psint = nullptr;}}Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;private:Singleton(int x = 0, int y = 0):_x(x),_y(y){}~Singleton(){//析构时,可能会需要将数据写到文件中去cout << "Singleton" << endl;}		int _x;int _y;static Singleton* _psint;//内部类,自动控制资源释放class GC{public:~GC(){Singleton::DelInstance();}};static GC gc;//类内声明};Singleton* Singleton::_psint = nullptr;Singleton::GC Singleton::gc;//类外定义
}

懒汉模式的优点:

  • 1. 不会影响程序的启动速度
  • 2. 可以手动控制单例对象的创建顺序

懒汉模式的缺点:

  • 1. 存在线程安全问题,需要加锁

设计方法2:
  在GetInstance接口中创建一个局部的静态成员变量用来存储资源,局部的静态成员变量声明周期是全局的。因为是局部的静态成员变量,所以,只有当第一次调用GetIsntance接口时才会创建对象。此方法在C++11前时无法使用的,在C++11以前此方法无法保证线程安全,C++11标准后才解决了这里的线程安全问题

namespace lazy
{class Singleton{public:static Singleton* GetInstance(){static Singleton _sinst;return &_sinst;}Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;private:Singleton(int x = 0, int y = 0):_x(x),_y(y){}~Singleton(){cout << "Singleton" << endl;}int _x;int _y;};
}

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

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

相关文章

【论文阅读】基于卷积神经网络和预提取特征的肌电信号分类

Myoelectric Signal Classification Using Convolutional Neural Networks with Pre-Extracted Features 原文&#xff1a;DOI: 10.1109/ICICS55353.2022.9811218 2022 翻译&#xff1a;靠岸学术 目录 摘要 1引言 2背景 A. 卷积神经网络 B. 特征工程 3材料与方法 A. CN…

珠海社保缴费记录如何打印

珠海社保掌上办&#xff08;微信小程序&#xff09; 进入“珠海社保掌上办”—“资料打印”— 选择养老工伤失业个人缴费证明&#xff0c;可选择 全部缴费记录打印或自选时段打印&#xff1a; 长按图片保存后打印。

AM32电调学习-使用Keil编译uboot

目前的AM32的APP固件包含了keil工程&#xff0c;但是uboot还没看到&#xff0c;对于习惯使用keil的新用户&#xff0c;调试起来会有些不习惯&#xff0c;本文将简单描述怎么新建一个keil的uboot工程&#xff0c;以AT32F421为例。一、新建目录新建一个目录Keil_Projects二、新建…

【大文件上传】分片上传+断点续传+Worker线程计算Hash

/*** 文件分片上传管理器* 提供文件分片、哈希计算、并发上传和断点续传功能*/ class FileChunkUploader {/*** 构造函数* param {File} file - 要上传的文件对象* param {Object} options - 配置选项* param {number} [options.chunkSize5MB] - 每个分片的大小&#xff08;字节…

-bash: ./restart.sh: /bin/bash^M: 坏的解释器: 没有那个文件或目录

这是典型的Windows换行符&#xff08;CRLF&#xff09;导致的脚本不能在Linux/Unix环境下正常执行的问题。Linux 期望的是 LF (\n)&#xff0c;而 Windows 是 CRLF (\r\n)&#xff0c;所以脚本文件的第一行解释器路径后多了一个不可见的 ^M&#xff08;回车符&#xff09;&…

芯伯乐1MHz高频低功耗运放芯片MCP6001/2/4系列,微安级功耗精密信号处理

前言在工业控制、通信设备、家用电器等领域&#xff0c;信号处理是核心环节之一&#xff0c;其中运算放大器&#xff08;运放&#xff09;是实现信号处理的核心器件&#xff0c;其选型参数直接决定了信号链路的性能和输出信号的质量&#xff0c;是确保信号正常、精确输出的关键…

智能的数学原理

智能的数学原理可以分成几个层次来看——从最底层的数学基础&#xff0c;到支撑“智能”表现的数学模型&#xff0c;再到连接数学与现实认知的理论框架。 分成 五个核心板块 来梳理&#xff1a;1. 信息与表示的数学 智能的第一步是“能表示信息”&#xff0c;这涉及&#xff1a…

FPGA即插即用Verilog驱动系列——SPI发送模块

实现功能&#xff1a;按字节以spi模式3发送数据&#xff0c;如果要stm32接收&#xff0c;请在cubemx中将对应的spi接口设置为模式3&#xff0c;详情见代码开头注释// spi_byte_master.v // 经过优化的SPI主设备模块&#xff0c;每次使能发送一个字节。 // 它实现了SPI模式3 (CP…

C++ 排序指南

在 C 中&#xff0c;std::sort 是一个非常强大且常用的函数&#xff0c;用于对容器或数组中的元素进行排序。它定义在 <algorithm> 头文件中。 std::sort 的基本语法 std::sort 的基本语法有以下几种形式&#xff1a;默认升序排序&#xff1a; std::sort(first, last);fi…

RS232串行线是什么?

RS232串行线是什么&#xff1f;RS232串行线是一种用于串行通信的标准化接口&#xff0c;广泛应用于早期计算机、工业设备、仪器仪表等领域的短距离数据传输。以下是其核心要点解析&#xff1a;1. 基本定义 全称&#xff1a;RS232&#xff08;Recommended Standard 232&#xff…

k8s-scheduler 解析

学习文档 官网的k8s上关于scheduler的文档基本可以分为这两部分 介绍 scheduler 的基本概念 介绍 scheduler 的配置 KubeSchedulerConfiguration 的参数 介绍 scheduler 的命令行参数 调度框架解析 Scheduling-framework 解析 kube-scheduler 选择 node 通过下面这两步…

前端简历1v1修改: 优化项目经验

今天有人找我优化前端简历&#xff0c;分享一下如何优化项目经验描述。这是修改前的版本&#xff1a;项目为Web前端开发&#xff0c;但描述为APP应用&#xff0c;包含某某功能。起初我感到困惑&#xff0c;因为前端技术栈使用Vue&#xff0c;为何项目类型是APP&#xff1f;后来…

K8S企业级应用与DaemonSet实战解析

目录 一、概述 二、YAML文件详解 三、企业应用案例 3.1 环境准备 3.2 扩缩容 3.3 滚动更新 3.4 回滚 四、自定义更新策略 4.1类型 4.2 设置方式 4.3 配置案例 一、 DaemonSet 概述 DaemonSet 工作原理 Daemonset 典型的应用场景 DaemonSet 与 Deployment 的区别…

Celery在Django中的应用

Celery在Django中的应用一、项目配置二、异步任务2.1 普通用法2.1.1 通过delay2.1.2 通过apply_async2.2 高级用法2.2.1 任务回调&#xff08;Callback&#xff09;2.2.2 任务链&#xff08;Chaining&#xff09;2.2.3 任务组&#xff08;Group&#xff09;2.2.4 任务和弦&…

DeepSeek生成的高精度大数计算器

# 高精度计算器&#xff08;精确显示版&#xff09;1. **精确显示优化**&#xff1a;- 新增print_mpfr()函数专门处理MPFR数值的打印- 自动移除多余的尾随零和小数点- 确保所有浮点结果都以完整十进制形式显示&#xff0c;不使用科学计数法2. **浮点精度修复**&#xff1a;- 所…

08--深入解析C++ list:高效操作与实现原理

1. list介绍1.1. list概述template < class T, class Alloc allocator<T> > class list;Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.概述&#xff1…

GraphQL从入门到精通完整指南

目录 什么是GraphQLGraphQL核心概念GraphQL Schema定义语言查询(Queries)变更(Mutations)订阅(Subscriptions)Schema设计最佳实践服务端实现客户端使用高级特性性能优化实战项目 什么是GraphQL GraphQL是由Facebook开发的一种API查询语言和运行时。它为API提供了完整且易于理…

使用 Dockerfile 与 Docker Compose 结合+Docker-compose.yml 文件详解

使用 Dockerfile 与 Docker Compose 结合的完整流程 Dockerfile 用于定义单个容器的构建过程&#xff0c;而 Docker Compose 则用于编排多个容器。以下是结合使用两者的完整方法&#xff1a; 1. 创建 Dockerfile 在项目目录中创建 Dockerfile 定义应用镜像的构建过程&#xff1…

15 ABP Framework 开发工具

ABP Framework 开发工具 概述 该页面详细介绍了 ABP Framework 提供的开发工具和命令行界面&#xff08;CLI&#xff09;&#xff0c;用于创建、管理和定制 ABP 项目。ABP CLI 是主要开发工具&#xff0c;支持项目脚手架、模块添加、数据库迁移管理及常见开发任务自动化。 ABP …

力扣top100(day02-01)--链表01

160. 相交链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {/*** 查找两个链表的相交节点* param headA 第一个…