【C++】特殊类实现
一、请设计一个类,不能被拷贝
拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,
只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
- C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
- C++11
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上
=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};
二、请设计一个类,只能在堆上创建对象
方法一:将类的析构函数私有。防止别人调用拷贝在栈上生成对象
编译报错:
栈上创建会自动调用析构,这里析构被私有了,就不能在栈上创建。
但这样在堆上创建后要进行delete也要调用析构函数,也就是虽然不能在栈上创建对象,但是同样也不能在堆上创建对象了。
解决方法:在类public中实现一个成员函数进行delete
class HeapOnly
{
public:void Destroy(){delete this;}
private:~HeapOnly(){//...}
};int main()
{//HeapOnly hp1;//static HeapOnly hp2;HeapOnly* hp3 = new HeapOnly;//delete hp3;hp3->Destroy();return 0;
}
编译成功:
方法二:
实现方式:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
完整代码:
class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}
private:HeapOnly(){//...}HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;
};int main()
{HeapOnly* hp3 = HeapOnly::CreateObj();//HeapOnly copy(*hp3);return 0;
}
以上代码有两个细节:
1. 如果没有提供静态的成员函数:
会发生报错,调用一个类的普通成员函数是不是需要对象去调用,那我们在堆上申请的对象又正好需要调用这个函数来生成,可是我们选择都没有对象我们如何去调用这个函数呢?所以这就纯纯是先有鸡还是先有蛋的问题了。
为了解决这个问题,我们可以将这个成员函数设置成静态的。
2. 利用拷贝构造在栈上创建报错:
虽然我们不能够直接的在栈上创建对象,但是我们可以在堆上申请一个对象之后再调用一次拷贝构造然后间接的就在栈上申请对象了。
所以我们的完整代码还要将拷贝构造和赋值重载都声明成delete禁掉,所以这里调用拷贝构造报错
三、请设计一个类,只能在栈上创建对象
方法:同上将构造函数私有化,然后设计静态方法创建对象返回即可。
下面先来看这种方式的实现:
编译报错,说明不能在堆上创建。
但是我们如果在new的时候调用拷贝构造:
编译通过:
我们发现又可以在堆上成功了,说明这种方法还有漏洞。
解决方法:下面我们来分析一下:new是由operator new + 构造组成的,所以我们只要在类中重载一个new,并将new声明成delete,就可以解决这个问题。
class StackOnly
{
public:static StackOnly CreateObj(){StackOnly st;return st;}
private:StackOnly(){//...}// 对一个类实现专属operator newvoid* operator new(size_t size) = delete;
};
运行发现报错,说明不能只有new在堆上创建对象了。
四、请设计一个类,不能被继承
- C++98方式
将构造函数私有:
基类的私有成员在派生类不可访问,而派生类要继承,需要调用基类的构造函数。所以将构造函数私有可以,让类不能被继承。
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
- C++11方法
final关键字,final修饰类,表示该类不能被继承。
class A final
{// ....
};
五、设计一个类,只能创建一个对象(单例模式)
设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
对于单例模式我们首先需要解决一个问题:如果保证全局(一个进程中)只有一个唯一实例对象
经过上面的学习我们就可以知道,要想保证全局只有一个唯一实例对象我们需要做以下两步操作
- 构造函数私有定义。拷贝构造和赋值防拷贝禁掉
- 提供一个GetInstance获取单例对象
单例模式有两种实现模式:饿汉模式与懒汉模式
饿汉模式:就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
下面来看饿汉模式的代码:
主要有以下三个关键步骤
1、构造函数私有 2、提供获取单例对象的接口函数 3、防拷贝
经过上面的学习我们就可以知道,为了防止被创建多个对象,所以我们要1、3这俩步,构造函数私有定义。拷贝构造和赋值防拷贝禁掉。
然后我们创建单例对象, 还要给它提供获取单例对象的接口函数。
// 饿汉模式:一开始(main函数之前)就创建单例对象
// 提供一个静态指向单例对象的成员指针,初始化时new一个对象给它
// 1、如果单例对象初始化内容很多,影响启动速度
// 2、如果两个单例类,互相有依赖关系。
// 假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A
namespace hungry
{class Singleton{public:// 2、提供获取单例对象的接口函数static Singleton& GetInstance(){return _sinst;}void func();void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}private:// 1、构造函数私有Singleton(){// ...}// 3、防拷贝Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;map<string, string> _dict;// ...static Singleton _sinst;};Singleton Singleton::_sinst;void Singleton::func(){// _dict["xxx"] = "1111";}
}int main()
{//Singleton s1;//Singleton s2;cout << &hungry::Singleton::GetInstance() << endl;cout << &hungry::Singleton::GetInstance() << endl;cout << &hungry::Singleton::GetInstance() << endl;//Singleton copy(Singleton::GetInstance());hungry::Singleton::GetInstance().Add({ "xxx", "111" });hungry::Singleton::GetInstance().Add({ "yyy", "222" });hungry::Singleton::GetInstance().Add({ "zzz", "333" });hungry::Singleton::GetInstance().Add({ "abc", "333" });hungry::Singleton::GetInstance().Print();return 0;
}
运行结果:
可以看到单例模式只能创建了一个对象
把其他的创建屏蔽掉再看运行结果:
可以看到GetInstance()之后地址都是相同的,说明只创建了一个对象。并且能实现里面简单的功能
饿汉模式适用场景:
- 如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
饿汉模式不适用场景:
- 如果单例类构造函数中,要做很多配置初始化工作,导致程序启动非常慢
- 如果两个单例类,互相有依赖关系。 假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A。我们如何保证先创建A再创建B呢?
这个时候我们使用饿汉就不合适了,这时我们应该使用下面的懒汉模式。
懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
下面来看这段懒汉的代码
namespace lazy
{//懒汉模式class Singleton{public:static Singleton* GetInstance(){if (_psinst == nullptr){_psinst = new Singleton;}return _psinst;}void Print(){cout << "Print()" << _a << endl;}private:Singleton():_a(0){}//C++98 防拷贝//Singleton(const Singleton&);//Singleton& operator=(const Singleton&);//C++11 防拷贝Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;int _a;static Singleton* _psinst;};Singleton* Singleton::_psinst = nullptr;}
但是这么写是会存在一些问题的。
在多线程场景下会存在线程安全的问题,因为我们的懒汉式要用的时候才会去调用GetInstance函数再去创建那个唯一的对象。
假如说现在有两个线程,分别是线程A与线程B,然后我线程A与线程B都调用了GetInstance函数,线程A与线程B都进入了if条件判断里面,此时我们线程A如果先执行实例化对象的代码,然后返回。因为我们的线程B不知道线程A已经实例化了唯一对象,此时线程B再去调用实例化对象的代码,就会导致前面的唯一对象的地址被覆盖掉,线程A的数据会丢失,并且会有内存泄漏的风险,更严重的是此时已经产生了两个实例对象,违反了单例模式的设计思想。
那我们应如何解决上面的问题呢?我们需要进行双检查加锁。
//懒汉模式
#include <mutex>
namespace lazy
{class Singleton{public:static Singleton* GetInstance(){// 保护第一次需要加锁,后面都不需要加锁的场景,可以使用双检查加锁// 特点:第一次加锁,后面不加锁,保护线程安全,同时提高了效率if (_pinst == nullptr){_mtx.lock();if (_pinst == nullptr){_pinst = new Singleton;}_mtx.unlock();}return _pinst;}static void DelInstance(){_mtx.lock();if (_pinst){delete _pinst;_pinst = nullptr;}_mtx.unlock();}void Print(){cout << "Print()" << _a << endl;}private:Singleton():_a(0){// 假设单例类构造函数中,要做很多配置初始化}~Singleton(){// 程序结束时,需要处理一下,持久化保存一些数据}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 实现一个内嵌垃圾回收类 class CGarbo {public:~CGarbo(){if (_pinst){delete _pinst;_pinst = nullptr;}}};int _a;static Singleton* _pinst;static std::mutex _mtx;static CGarbo _gc;};Singleton* Singleton::_pinst = nullptr;std::mutex Singleton::_mtx;Singleton::CGarbo Singleton::_gc;
}
为什么是双检查加锁?
我们需要知道我们想加锁的地方只是第一次 _pinst为空的时候,我们想实例化一个唯一对象时才需要加锁,但是如果到了后面我们已经实例化出了对象,但是我们照样加锁了之后再去判断 _pinst是否为空,然后在多线程场景下频繁的加锁解锁会导致效率降低,并且也不推荐这样做。
而我们的双检查加锁里面的第二个if检查就是我先进行加锁,然后进行判断第一次_pinst是否为空,然后实例化一个唯一对象,等到下一次某个线程再调用GetInstance的时候,我们的第一个if检查发现pinst不为空,就不会进入第一个if条件判断的里面,因此也就不存在多线程场景下频繁加锁解锁,保护线程安全,同时提高了效率。
饿汉模式和懒汉模式的优缺点对比如下:
饿汉模式:
- 优点:实例化对象在类加载时完成,因此无须考虑多线程访问问题,可以确保实例的唯一性。
- 缺点:
- 饿汉式单例对象在类加载时就被创建,可能会造成系统资源的浪费,尤其是在对象占内存较大或对象初始化耗时较长的情况下。
- 如果有多个单例对象,他们之间有初始化依赖关系,饿汉模式也会有问题。(比如有A和B两个单例类,要求A单例先初始化,B必须在A之后初始化。那么饿汉无法保证。这种场景下面用懒汉就可以,懒汉可以先调用A::GetInstance(),再调用B::GetInstance().)
懒汉模式:
- 优点:实现了延迟加载,只有在第一次使用时才创建实例,节省了系统资源。(解决饿汉的缺点。因为他是第一次调用GetInstance时创建初始化单例对象)
- 缺点:必须考虑与处理多个线程同时访问的问题,需要进行双重检查锁定等机制及进行控制。
相关文章:

【C++】特殊类实现
一、请设计一个类,不能被拷贝 拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝, 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98 将拷贝构造函数与赋值运算符重载只声明不定义…...
代码随想录打卡第四十四天|● 01 二维背包问题 ●一维背包问题-滚动数组 ● 416. 分割等和子集
什么是01背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 01背包的模板 二维dp数组 dp数组的含义 dp[i][j]含义下标为【0-i】之间…...

燃气管网智能巡检系统
燃气管网维护工作繁杂,涉及人员、资源、巡检等,稍一疏忽就会使我们的工作陷入被动,可见启用燃气管网智能巡检系统是很有必要的。 燃气管网智能巡检系统综合管理智能平台,可对燃气管网数据的统一管理,实现对日常巡查、养…...

【微信小程序开发】运用WXS进行后台数据交互
🥳🥳Welcome Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于小程序的相关操作吧 一.wxs是什么 WXS是指"微信小程序云开发"(WeChat Mini Program Cloud Development),是由微信…...

屏幕录像推荐:Apeaksoft Screen Recorder 中文 for mac
Apeaksoft Screen Recorder 是一款功能强大的屏幕录制软件,它允许用户在 Windows 和 Mac 系统上捕捉和录制屏幕活动。无论是记录游戏过程、创建教学视频、制作演示文稿还是捕捉在线流媒体内容,该软件都提供了丰富的功能和工具。 以下是 Apeaksoft Scree…...
ALPHA开发板网络方案说明
一. 简介 正点原子 ALPHA开发板,包括我们移植的 Uboot,都是参考了 NXP(恩智浦)官方的开发板的。 I.MX6UL/ULL 内部有个以太网 MAC 外设,也就是 ENET ,需要外接一个 PHY 芯片来实现网络通信功能&#…...
[Ubuntu 20.04] HEIF图像格式与libheif库及其工具的使用
一、HEIF图像格式 HEIF 是一种高效的图像文件格式,它由 MPEG(Moving Picture Experts Group)组织制定。相较于传统的 JPEG 格式,HEIF 提供了更好的图像质量和更高的压缩率。下面是对 HEIF 格式的详细解析: 图像编码技术:HEIF 使用先进的编码技术来实现更高效的图像压缩。…...

AI驱动的未来:探索人工智能的无限潜力 | 开源专题 No.39
这一系列开源项目代表着多个领域的最新技术成果,包括深度学习、自然语言处理、计算机视觉和分布式训练。它们共同的特点是致力于教育、资源分享、开源精神、多领域应用以及性能和效率的追求,为广大开发者、研究者和学生提供了宝贵的工具和知识࿰…...
vs中C++编译未生成exe
1、新建空工程,添加main.h文件至“头文件”文件夹中,添加mian函数及实现 2、编译工程未有任何提示,不报错,不生成exe,无法执行 对比新建控制台程序发现.vcxproj文件中引用main.h文件为 无法生成: <I…...

Linux自有服务与软件包管理
服务是一些特定的进程,自有服务就是系统开机后就自动运行的一些进程,一旦客户发出请求,这些进程就自动为他们提供服务,windows系统中,把这些自动运行的进程,称为"服务" 举例:当我们使…...
Centos7中redis开机自启动设置
以下亲测实践有效。 进入以下目录 cd usr/local/redis/redis-6.2.6/utils/ 编辑修改以下文件内容 vim redis_init_script #修改redis安装启动目录 REDISPORT6379 #修改安装目录 EXEC/usr/local/redis/redis-6.2.6/src/redis-server CLIEXEC/usr/local/redis/redis-6.2.6/sr…...

STM32F4之系统滴答定时器
一、系统滴答定时器概述 传统定时器:如手机闹钟,闹钟等就是一个简单地计数器。 定时器概念:由时钟源计数器计数值组成的计数单元。 系统嘀嗒定时器首先是存在于内核里,系统嘀嗒时钟假如用的是同一个内核那么里面相关的配置&…...
P4 并发控制
文章目录 Task1 锁管理器LockTableUnLockTableLockRowUnLockRow Task2 死锁检测Task3 并发查询执行器Isolation Levelseq_scan_executorinsert_executordelete_executortransaction_manager Task1 锁管理器 LockManager类包含两个属性类,分别是LockRequest和LockRe…...
友元的介绍
实现外部类和外部函数存取类的私有成员和保护成员的方法。 一、友元函数 可访问类所有成员的外部函数 //求两点间的距离:抽象点——>求距离的函数 #include<iostream> #include<cmath> using namespace std; class Point{private:double x,y;publ…...

新手如何找到Docker容器(redis)中的持久化文件?
具体步骤 要查看Docker容器的dump.rdb和appendonly.aof文件(如果启用了AOF持久化)的位置,我们需要知道容器中Redis配置文件的内容或者容器的数据卷的挂载位置。 这里是一般步骤: 查找容器的数据卷挂载位置 使用docker inspect命令…...

python二次开发Solidworks:读取立方体的高度
在SW中新建一个零件文档,建立一个立方体,长度和宽度自定义,高度100mm,下面通过python实现读取该立方体的高度: import win32com.client as win32 import pythoncomswApp win32.Dispatch(sldworks.application) swApp.…...
NPM安装后报错:ERROR: npm v10.2.1 is known not to run on Node.js v10.24.1.
问题描述 NPM卸载高版本后安装低版本运行报错: C:\Users\Administrator>npm -v ERROR: npm v10.2.1 is known not to run on Node.js v10.24.1. This version of npm supports the following node versions: ^18.17.0 || >20.5.0. You can find the latest…...
【Vue】Element开发笔记
Element开发笔记 前言 官网 https://element.eleme.cn/#/zh-CN/component/upload 其它项目网站 https://www.cnblogs.com/qq2806933146xiaobai/p/17180878.html 表格 序号列添加 <el-table-column type"index" :index"handleIndexCalc" label&qu…...
How to install mongodb 7.0 to Ubuntu 22.04
How to install mongodb 7.0 to Ubuntu 22.04 1、安装1.1、添加gpg1.2、添加apt源1.3、更新1.4、安装 2、管理2.1、服务管理2.1.1、查看服务状态2.1.2、启动服务2.1.3、 设置服务为开机启动2.1.4、取消服务开机启动2.1.5、关闭服务2.1.6、服务重启 2.2、mongosh2.2.1、进入mong…...

AFL安全漏洞挖掘
安全之安全(security)博客目录导读 ATF(TF-A)/OPTEE之FUZZ安全漏洞挖掘汇总 目录 一、AFL简介 二、AFL的安装 三、代码示例及种子语料库 四、AFL插桩编译 五、AFL运行及测试 六、AFL结果分析 一、AFL简介 模糊测试(Fuzzing)技术作为漏洞挖掘最有…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...