【C++】特殊类设计

在这里插入图片描述

目录

  • 一、请设计一个类,不能被拷贝
  • 二、请设计一个类,只能在堆上创建对象
  • 三、请设计一个类,只能在栈上创建对象
  • 四、请设计一个类,不能被继承
  • 五、请设计一个类,只能创建一个对象(单例模式)
    • 5.1 饿汉模式
    • 5.2 懒汉模式
  • 结尾

一、请设计一个类,不能被拷贝

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

  1. C++98

    私有 + 只声明不定义

    • 私有:若只声明不定义并且在没有私有的情况下,用户可以在外面进行定义
    • 只声明不定义:不声明,操作系统会生成默认的拷贝构造和拷贝复制函数,定义了就不能防止拷贝了。
  2. C++11

    在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。

class BanCopy
{
public:// 默认构造函数BanCopy(){}// C++11 默认成员函数后跟上 = delete// 拷贝构造BanCopy(const BanCopy& bc) = delete;// 拷贝赋值BanCopy& operator=(const BanCopy& bc) = delete;private:// C++ 98 私有 + 只声明不定义// 若只声明不定义并且在没有私有的情况下,用户可以在外面进行定义/*BanCopy(const BanCopy& bc);BanCopy operator=(const BanCopy& bc);*/
};int main()
{BanCopy bc1;BanCopy bc2;BanCopy bc3(bc1);bc2 = bc1;return 0;
}

在这里插入图片描述

二、请设计一个类,只能在堆上创建对象

实现方式1:

  1. 构造函数只声明不定义并私有化,拷贝构造函数只声明不定义并私有化,防止通过拷贝构造在栈上创建对象。
  2. 定义一个静态函数,用来提供在堆上创建对象。
class OnlyHeap
{
public:static OnlyHeap* CreateObject(){return new OnlyHeap;}private:// 默认构造OnlyHeap(){}// 拷贝构造OnlyHeap(const OnlyHeap& oh){}
};int main()
{OnlyHeap oh1;OnlyHeap* oh2 = OnlyHeap::CreateObject();OnlyHeap oh3(*oh2);return 0;
}

在这里插入图片描述

实现方式2:

析构函数只声明不定义并私有化,析构函数是私有的,那么在对象离开其作用域时,编译器试图调用析构函数时会遇到问题,因为它不能从外部访问私有成员。

class OnlyHeap
{
public:// 默认构造OnlyHeap(){}static OnlyHeap* CreateObject(){return new OnlyHeap;}private:// 拷贝构造OnlyHeap(const OnlyHeap& oh){}~OnlyHeap(){}
};int main()
{// OnlyHeap oh1;OnlyHeap* oh2 = OnlyHeap::CreateObject();OnlyHeap oh3(*oh2);return 0;
}

在这里插入图片描述

三、请设计一个类,只能在栈上创建对象

class OnlyStack
{
public:static OnlyStack CreateStack(){OnlyStack os;return os;}// 这里不能将拷贝构造删除// 因为CreateStack函数是传值返回,需要拷贝构造// OnlyStack(const OnlyStack& os) = delete;
private:OnlyStack(){}// new分为构造和operator new 两个部分// 我们已经对构造函数动手了,拷贝构造又不能动// 那么接下来只有对operator new动手了// 实现专属的operator new// 那么new这个对象的时候就不会调用全局的,而是调用这里的/*void* operator new(size_t size){cout << "void* operator new(size_t)" << endl;return malloc(size);}*/// 我们将operator new删除了,那么就new不了对象了void* operator new(size_t) = delete;
};int main()
{OnlyStack os1 = OnlyStack::CreateStack();// OnlyStack* os2 = new OnlyStack;OnlyStack* os3;os3 = new OnlyStack(os1);return 0;
}

在这里插入图片描述

四、请设计一个类,不能被继承

  1. C++98
    由于派生类的构造需要调用基类的构造函数,而这里将构造函数私有后,派生类则不能调用基类的构造函数,那么该类不能被继承。
class CannotInherit
{
public:static CannotInherit CreateObject(){return CannotInherit();}private:CannotInherit(){}
};class DerivedClass : public CannotInherit
{DerivedClass(){}
};

在这里插入图片描述

  1. C++11
    final修饰类,表示该类不能被继承。

在这里插入图片描述

五、请设计一个类,只能创建一个对象(单例模式)

设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

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

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

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

5.1 饿汉模式

就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象

饿汉模式的优缺点

  • 优点:简单
  • 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
// 饿汉模式程序启动时就创建一个唯一的实例对象
// 那么什么变量能够在进入main函数之前就定义呢
// 那么就是全局变量了
// 但是下面单例模式中的构造函数私有化了
// 导致外面的无法构造对象了
// 全局变量不行,那么就可以使用静态变量了
// 在单例模式中添加一个该类的静态类对象
// 在类中声明,在类外定义class SingLeton
{static SingLeton* GetInstance(){return &_sl;}private:// 私有化构造函数SingLeton(){}// 防拷贝// 删除拷贝构造SingLeton(const SingLeton& sl) = delete;// 删除拷贝赋值SingLeton& operator=(const SingLeton& sl) = delete;private:static SingLeton _sl;
};SingLeton SingLeton::_sl;

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

那么这里单例模式中的饿汉模式就完成了,需要某个资源只创建一个对象,那么就在单例模式中添加这个资源的成员变量即可。


5.2 懒汉模式

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

懒汉模式的优缺点

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

假设下面懒汉模式下存储的对象是map

// 懒汉模式第一次使用实例对象时,创建对象
// 所以我们在类里面定义一个该类的静态指针类型
// 再在类外面对该指针初始化为nullptr
// 当第一个使用一个类时,再创建对象
class SingLeton
{
public:static SingLeton* GetInstance(){if (_psl == nullptr){_psl = new SingLeton;}return _psl;}void Insert(const string& key, const string& value){_dict[key] = value;}void Print(){for (auto dict : _dict){cout << dict.first << ':' << dict.second << endl;}cout << endl;}private:// 私有化构造函数SingLeton(){}private:static SingLeton* _psl;map<string, string> _dict;};SingLeton* SingLeton::_psl = nullptr;

那么为什么需要删除拷贝构造和拷贝赋值呢?

int main()
{SingLeton::GetInstance()->Insert("want","想");SingLeton::GetInstance()->Insert("miss", "错过");SingLeton::GetInstance()->Insert("left", "左边");SingLeton p(*(SingLeton::GetInstance()));p.Insert("miss", "想念");SingLeton::GetInstance()->Print();p.Print();return 0;
}

在这里插入图片描述
因为不删除拷贝构造和拷贝赋值会导致单例不具有唯一性。

删除拷贝构造和拷贝赋值后的懒汉模式。

// 懒汉模式第一次使用实例对象时,创建对象
class SingLeton
{
public:static SingLeton* GetInstance(){if (_psl == nullptr){_psl = new SingLeton;}return _psl;}void Insert(const string& key, const string& value){_dict.insert(make_pair(key, value));}void Print(){for (auto dict : _dict){cout << dict.first << ':' << dict.second << endl;}}private:// 私有化构造函数SingLeton(){}// 防拷贝// 删除拷贝构造SingLeton(const SingLeton& sl) = delete;// 删除拷贝赋值SingLeton& operator=(const SingLeton& sl) = delete;private:static SingLeton* _psl;map<string, string> _dict;
};

依照上面的代码还可以看出一个问题,那就是_psl是new出来的,需要delete吗?如何delete呢?

一般来说单例模式是伴随着一整个程序的,程序结束后会自动释放,不排除单例模式用到一半后不需要它了的情况,也有可能是程序结束后需要对数据进行持久化,所以可能需要delete,那么如何delete呢?

首先可以想到这个delete SingLeton::GetInstance(),但是这个写法太挫了,别人可能看漏或是不理解,在后面继续使用但是模式,那么下面这段代码教大家如何delete这段数据。

首先在SingLeton中定义一个函数static void DelInstance()用来释放空间,再定义一个内部类InstanceCleaner,而这个类是空类,在SingLeton中声明一个静态的InstanceCleaner类对象,在类外面定义,由于是空类,并不用担心会影响程序启动的速度,在InstanceCleaner的析构函数中调用DelInstance,那么在程序结束后会释放InstanceCleaner对象,调用析构函数,再调用DelInstance释放数据。值得注意的是SingLeton对象中的数据可以在程序结束后,依靠InstanceCleaner来释放数据,也可以自己手动调用DelInstance提前释放数据。

class SingLeton
{
public:static SingLeton* GetInstance(){if (_psl == nullptr){_psl = new SingLeton;}return _psl;}static void DelInstance(){if (_psl){delete _psl;_psl = nullptr;cout << "static void DelInstance()" << endl;}}void Insert(const string& key, const string& value){_dict.insert(make_pair(key, value));}void Print(){for (auto dict : _dict){cout << dict.first << ':' << dict.second << endl;}}class InstanceCleaner{public:InstanceCleaner(){}~InstanceCleaner(){DelInstance();}};private:// 私有化构造函数SingLeton(){}~SingLeton(){}// 防拷贝// 删除拷贝构造SingLeton(const SingLeton& sl) = delete;// 删除拷贝赋值SingLeton& operator=(const SingLeton& sl) = delete;
private:static SingLeton* _psl;map<string, string> _dict;static InstanceCleaner _InstanceCleaner;
};SingLeton* SingLeton::_psl = nullptr;
SingLeton::InstanceCleaner _InstanceCleaner;
int main()
{SingLeton::GetInstance()->Insert("want","想");SingLeton::GetInstance()->Insert("miss", "错过");SingLeton::GetInstance()->Insert("left", "左边");cout << "Hello C++" << endl;SingLeton::GetInstance()->DelInstance();return 0;
}

在这里插入图片描述


实际上上面的懒汉模式还存在线程安全问题,在创建和删除单例的时候,多个线程可能会同时进入,这里我们保证只有第一个调用的线程才可以创建或是删除的单例。

// 懒汉模式第一次使用实例对象时,创建对象
#include <mutex>
class SingLeton
{
public:static SingLeton* GetInstance(){// 防止浪费锁资源if (_psl == nullptr){// 防止多个线程同时进入unique_lock<mutex> lock(mtx);if (_psl == nullptr){_psl = new SingLeton;}}return _psl;}static void DelInstance(){if (_psl){unique_lock<mutex> lock(mtx);if (_psl){delete _psl;_psl = nullptr;cout << "static void DelInstance()" << endl;}}}void Insert(const string& key, const string& value){_dict.insert(make_pair(key, value));}void Print(){for (auto dict : _dict){cout << dict.first << ':' << dict.second << endl;}}class InstanceCleaner{public:InstanceCleaner(){}~InstanceCleaner(){DelInstance();}};private:// 私有化构造函数SingLeton(){}~SingLeton(){}// 防拷贝// 删除拷贝构造SingLeton(const SingLeton& sl) = delete;// 删除拷贝赋值SingLeton& operator=(const SingLeton& sl) = delete;
private:static SingLeton* _psl;static mutex mtx;map<string, string> _dict;static InstanceCleaner _InstanceCleaner;
};SingLeton* SingLeton::_psl = nullptr;
SingLeton::InstanceCleaner _InstanceCleaner;

这里我们设计一个最简单的单例模式,下面的并且下面的代码是懒汉模式,只有第一次调用函数时,才会创建单例。sl是一个静态对象,只有在第一次调用的时候才会初始化,下面的代码在C++11之前是存在线程安全问题的,这也是C++11之前的缺陷,而C++11之后就不存在线程安全问题了,保证了sl只初始化一次。

class SingLeton
{
public:static SingLeton& GetInstance(){static SingLeton sl;return sl;}private:// 私有化构造函数SingLeton(){}~SingLeton(){}// 防拷贝// 删除拷贝构造SingLeton(const SingLeton& sl) = delete;// 删除拷贝赋值SingLeton& operator=(const SingLeton& sl) = delete;
};

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

在这里插入图片描述

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

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

相关文章

SSM开发(七) MyBatis解决实体类(model)的字段名和数据库表的列名不一致方法总结(四种方法)

目录 方法一: 使用@Results和@Result注解(注解方式) 方法二:修改 SQL 查询语句中的别名(注解方式) 方法三: 全局配置别名或结果映射(resultMap,XML配置方式) 方法四:使用@Column注解 在MyBatis中,如果你希望使用注解的方式来操作数据库,但又遇到实体类中的…

USB 3.1-GL3510-52芯片原理图设计

USB 3.1-GL3510-52芯片原理图设计 端口功能与兼容性物理层集成与性能电源相关特性充电功能其他特性原理图接口防护ESD 保护要求 GL3510-52是一款由Genesys Logic&#xff08;创惟科技&#xff09;研发的USB转换芯片&#xff0c;具有以下特点&#xff1a; 端口功能与兼容性 它…

LeetCode热题100中 17. 20. 53. 78. 215.

17.电话号码的字母组合&#xff1a; 题目描述&#xff1a; 实现思路&#xff1a; 将回溯过程抽象成树结构&#xff0c;每个叶子节点作为结果的一部分。 我们定义一个数组map&#xff0c;它的下标表示输入的数字所对应的字母&#xff0c;先对特殊情况进行处理&#xff1a;1.输…

高级编码参数

1.跳帧机制 参考资料&#xff1a;frameskipping-hotedgevideo 跳帧机制用于优化视频质量和编码效率。它通过选择性地跳过某些帧并使用参考帧来预测和重建视频内容&#xff0c;从而减少编码所需的比特率&#xff0c;同时保持较高的视频质量。在视频编码过程中&#xff0c;如果…

内网穿透实现MC联机

目录 内网穿透下载安装服务端&#xff08;你&#xff09;启动网络启动 MC 客户端&#xff08;你的朋友&#xff09; 放寒假了&#xff0c;想和同学玩mc&#xff0c;但是没有服务器怎么办呢&#xff1f;这就不得不提到内网穿透技术了。 注&#xff1a;本文参考视频&#xff1a;…

【每日一A】2015NOIP真题 (二分+贪心) python

题目概述 在起点和终点之间有n个石头&#xff0c;移除某些&#xff08;不超过m个&#xff09;石头后&#xff0c;让石头间的距离最大。 求石头间的最短距离d的最大值 跳石头 点此跳转 https://www.lanqiao.cn/problems/364/learning/?page1&first_category_id1&status…

获取snmp oid的小方法1(随手记)

snmpwalk遍历设备的mib # snmpwalk -v <SNMP version> -c <community-id> <IP> . snmpwalk -v 2c -c test 192.168.100.201 .根据获取的值&#xff0c;找到某一个想要的值的oid # SNMPv2-MIB::sysName.0 STRING: test1 [rootzabbix01 fonts]# snmpwalk -v…

FreeRTOS从入门到精通 第十四章(队列集)

参考教程&#xff1a;【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili 一、队列集简介 1、队列集概述 &#xff08;1&#xff09;一个队列只允许任务间传递的消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;那么就可以…

Spring MVC 综合案例

目录 一. 加法计算器 1. 准备工作 2. 约定前后端交互接口 需求分析 接口定义 3. 服务器端代码 4. 运行测试 二. 用户登录 1. 准备工作 2. 约定前后端交互接口 需求分析 接口定义 (1) 登录界面接口 (2) 首页接口 3. 服务器端代码 4. 运行测试 三. 留言板 1. 准备…

Edge-TTS在广电系统中的语音合成技术的创新应用

Edge-TTS在广电系统中的语音合成技术的创新应用 作者&#xff1a;本人是一名县级融媒体中心的工程师&#xff0c;多年来一直坚持学习、提升自己。喜欢Python编程、人工智能、网络安全等多领域的技术。 摘要 随着人工智能技术的快速发展&#xff0c;文字转语音&#xff08;Te…

36、【OS】【Nuttx】OSTest分析(2):环境变量测试

背景 2025.1.29 蛇年快乐&#xff01; 接之前wiki 35、【OS】【Nuttx】OSTest分析&#xff08;1&#xff09;&#xff1a;stdio测试&#xff08;五&#xff09; 已经分析完了第一个测试项&#xff0c;输入输出端口测试&#xff0c;接下来分析下环境变量测试&#xff0c;也比较…

设计模式的艺术-策略模式

行为型模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解策略模式 在策略模式中&#xff0c;可以定义一些独立的类来封装不同的算法&#xff0c;每个类封装一种具体的算法。在这里&#xff0c;每个封装算法的类都可以称之为一种策略&#xff08;Strategy…

Oracle迁移DM数据库

Oracle迁移DM数据库 本文记录使用达梦官方数据迁移工具DTS&#xff0c;将Oracle数据库的数据迁移至达梦数据库。 1 数据准备 2 DTS工具操作步骤 2.1 创建工程 打开DTS迁移工具&#xff0c;点击新建工程&#xff0c;填写好工程信息&#xff0c;如图&#xff1a; 2.2 新建迁…

Base64详解

文章目录 Base64详解一、引言二、Base64编码原理1、Base64的基本概念2、编码过程2.1、分组与填充2.2、二进制转换2.3、字符映射 三、Base64解码原理四、使用示例1、Java实现Base64编码2、Java实现Base64解码 五、总结 Base64详解 一、引言 Base64是一种常见的编码方式&#xf…

Android createScaledBitmap与Canvas通过RectF drawBitmap生成马赛克/高斯模糊(毛玻璃)对比,Kotlin

Android createScaledBitmap与Canvas通过RectF drawBitmap生成马赛克/高斯模糊&#xff08;毛玻璃&#xff09;对比&#xff0c;Kotlin import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.RectF …

消息队列篇--通信协议篇--应用层协议和传输层协议理解

在网络通信中&#xff0c;传输层协议和应用层协议是OSI模型中的两个不同层次的协议&#xff0c;它们各自承担着不同的职责。 下文中&#xff0c;我们以TCP/UDP&#xff08;传输层协议&#xff09;和HTTP/SMTP&#xff08;应用层协议&#xff09;为例进行详细解释。 1、传输层协…

团体程序设计天梯赛-练习集——L1-025 正整数A+B

一年之际在于春&#xff0c;新年的第一天&#xff0c;大家敲代码了吗&#xff1f;哈哈 前言 这道题分值是15分&#xff0c;值这个分&#xff0c;有一小点运算&#xff0c;难度不大&#xff0c;虽然说做出来了&#xff0c;但是有两个小疑点。 L1-025 正整数AB 题的目标很简单…

登录授权流程

发起一个网络请求需要&#xff1a;1.请求地址 2.请求方式 3.请求参数 在检查中找到request method&#xff0c;在postman中设置同样的请求方式将登录的url接口复制到postman中&#xff08;json类型数据&#xff09;在payload中选择view parsed&#xff0c;将其填入Body-raw中 …

护眼好帮手:Windows显示器调节工具

在长时间使用电脑的过程中&#xff0c;显示器的亮度和色温对眼睛的舒适度有着重要影响。传统的显示器调节方式不仅操作繁琐&#xff0c;而且在低亮度下容易导致色彩失真。因此&#xff0c;今天我想为大家介绍一款适用于Windows系统的护眼工具&#xff0c;它可以帮助你轻松调节显…

51单片机开发:独立键盘实验

实验目的&#xff1a;按下键盘1时&#xff0c;点亮LED灯1。 键盘原理图如下图所示&#xff0c;可见&#xff0c;由于接GND&#xff0c;当键盘按下时&#xff0c;P3相应的端口为低电平。 键盘按下时会出现抖动&#xff0c;时间通常为5-10ms&#xff0c;代码中通过延时函数delay…