[c++进阶(三)]单例模式及特殊类的设计
1.前言
在实际场景中,总会遇见一些特殊情况,比如设计一个类,只能在堆上开辟空间,
或者是设计一个类只能实例化一个对象。那么我们应该如何编写代码呢?本篇将会详细的介绍
本章重点:
本篇文章着重讲解如何设计一些特殊
的类,包括不能被拷贝,只能在栈/堆上
创建对象以及此类只能实例化一个对象,
这也是我们听说过的单例模式,单例模式又
包含饿汉和懒汉模式,文章都是干货
请同学们耐心学习!
2.设计一个不能被拷贝/继承的类
1.设计一个类,不能被拷贝
在设计这个类之前,需要明确的是:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝, 只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
在c++98中一般操作就是在私有中把这两个函数只声明,不定义。
class CopyBan
{private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);
};
原因:
class CopyBan
{CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;
};
2.设计一个不能被继承的类
c++11中可以直接使用关键字final
class A final
{// ....
};
c++98:基类的构造函数私有化,这样子类在创建对象时,属于基类的那一部分会调用基类的构造函数初始化,那么由于基类的构造函数私有化了,因此无法初始化,直接报错。
class NonInherit
{
private:NonInherit(){}
};
3.设计只能在堆上创建对象的类
只能在堆上创建对象,那么就表明所有创建的对象经过的构造函数都要用new来构造,这样才能够满足要求。
除此之外还要禁止拷贝构造和赋值函数,因为这样可以防止别人调用拷贝或者赋值重载函数来生成对象
class HeapOnly
{
public: static HeapOnly* CreateObject() { return new HeapOnly; }
private: HeapOnly() {}// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&);// or// C++11 HeapOnly(const HeapOnly&) = delete;
};
解释为什么这里使用的是static:
将构造函数设置为私有后,不管是在堆上还是栈上都不能创建对象,但是我们可以在共有域写一个函数显示去调用构造函数,注意,这里的共有域函数必须设置为static类型,因为必须有了对象后才能调用函数,但是要调用了此函数才能创建对象,就会出现先有鸡还是先有蛋的问题,所以设置为static后,可以用类域调用!
方法二:将析构函数封掉。
先解释为什么这个方法可行。一般来说:
C++是静态绑定的语言,在编译时期,所有的非虚函数调用都必须分析完成。则当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期(具体过程为: 通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数 )。所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性(其实不光是析构函数,只要是非静态的函数,编译器都会进行检查)。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。 所以 析构函数私有化的类的设计可以保证只能用new命令在堆(heap)中创建对象。
//封析构函数
class HeapOnly
{
public:void destory(){delete this;}
private:~HeapOnly(){cout<<"调用析构成功!"<<endl;}
};
4.设计只能在栈上创建对象的类
只能在栈上创建对象的话,那么就必须把new 和delete函数给封掉。因为new这个函数创建的对象一定是在堆上的。那其他的设计思路就和上述设计只能在堆上创建对象类似了
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
// StackOnly obj = StackOnly::CreateObj();
// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly() :_a(0){}private:int _a;
};
5.只能实例化一个对象的类
一个类只能实例化出一个对象,这就是大名鼎鼎的“单例模式”。
在阐述单例模式之前,我们先需要了解一下什么是设计模式。

什么是单例模式呢?

单例模式的运用是非常的广泛的,如在线程池里面等等,可以自行去搜索了解。
单例模式有两种实现的方式:1 .饿汉模式,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象
2.懒汉模式:只有在使用的时候,才初始化出对象。
6.饿汉模式
注意,这里实现的是样例(demo)代码,在
不同的工程场景下需要大家做灵活的变换
在设计之前先明确的是:什么情况下才会只初始化出一个对象呢?不管其他在怎么做,都不会初始化其他的对象。---静态成员函数及变量。只会初始化一次。---于是单例模式就可以设计成静态函数。
// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:static Singleton* GetInstance(){return _ins;}
private://限制类外随意创建对象Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;Singleton(){}
private:static Singleton* _ins;
};
Singleton* Singleton::_ins = new Singleton;
注意:类里面的静态成员变量只能在类中声明,定义必须在类的外面。
单例模式的饿汉模式中,程序一启动就会把_ins,也就是唯一的实例对象给初始化,
并且由于构造函数被私有了,只能调用共有的GetInstance()函数获取_ins对象,又
由于这个对象是static类型的,所以不管你调用多少次GetInstance()都获取的是同
一个对象,也就是_ins
7.懒汉模式
//懒汉模式
class Singleton
{
public:static Singleton* GetInstance(){if (_ins == nullptr){_ins = new Singleton;}return _ins;}void DelInstance(){if (_ins != nullptr){cout << "over!!!" << endl;delete _ins;_ins = nullptr;}}
private://限制类外随意创建对象Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;Singleton(){}
private:static Singleton* _ins;};
Singleton* Singleton::_ins = nullptr;
但是这样在多线程的情况下会有一个问题,多个线程同时进入getinstance函数,这就导致多个线程都生成了一个对象。这和我们要求的一个类只能创建一个对象时不符合的。所以说我们要对这种不是原子的操作进行加锁(原子的操作学linux和mysql时会讲到)
修改代码如下:
//懒汉模式
class Singleton
{
public:static Singleton* GetInstance(){if (_ins == nullptr)//双检查加锁,只有第一次进来时需要加锁,其他情况不用加锁{mtx.lock();if (_ins == nullptr)//第一次调用才创建实例!{_ins = new Singleton;}mtx.unlock();}return _ins;}void DelInstance(){mtx.lock();if (_ins != nullptr){cout << "over!!!" << endl;delete _ins;_ins = nullptr;}mtx.unlock();}
private://限制类外随意创建对象Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;Singleton(){}
private:static Singleton* _ins;static mutex mtx;
};
Singleton* Singleton::_ins = nullptr;
mutex Singleton::mtx;
8.总结与拓展
特殊类的设计和单例模式就讲到这了,只单说概念其实大家对于单例模式的强大性还是理解的不够深刻,只有在结合项目一起使用时,那么理解就会快速增加了。
拓展:单例模式只是设计模式中的一种,还有工厂模式,适配器模式等等,有想了解这些设计模式的可以自行查阅资料。
设计模式 - 概览 | C++ 全栈知识体系 (stibel.icu)
相关文章:
[c++进阶(三)]单例模式及特殊类的设计
1.前言 在实际场景中,总会遇见一些特殊情况,比如设计一个类,只能在堆上开辟空间, 或者是设计一个类只能实例化一个对象。那么我们应该如何编写代码呢?本篇将会详细的介绍 本章重点: 本篇文章着重讲解如何设计一些特殊 的类,包括不能被拷贝,只能在栈/堆上…...
企业内训|高智能数据构建和多模态数据处理、Agent研发及AI测评技术内训-吉林省某汽车厂商
吉林省某汽车厂商为提升员工在AI大模型技术方面的知识和实践能力,举办本次为期8天的综合培训课程。本课程涵盖“高智能数据构建与智驾云多模态数据处理”、“AI Agent的研发”和“大模型测评”三大模块。通过系统梳理从非结构化数据的高效标注与融合,到L…...
009 Qt_显示类控件_QLCDNumber、ProgressBar、Calendar
文章目录 前言LCD NumberProgressBarCalendar Widget 小结 前言 本文将会向你介绍显示类控件中QLCDNumber显示数字、ProgressBar进度条、Calendar日历 LCD Number QLCDNumer 是⼀个专门用来显示数字的控件. 类似于 “老式计算器” 的效果. 属性说明intValueQLCDNumber 显示…...
--spring.profiles.active=prod
rootproduct-qualification:~# ps -ef | grep java root 5110 1 3 16:57 ? 00:00:54 java -jar productQualification.jar --spring.profiles.activeprod root 6476 5797 0 17:26 pts/0 00:00:00 grep --colorauto java好的,你使用 ps …...
深入解析JVM中对象的创建过程
1. 引言 对象是面向对象编程的核心概念之一,它们封装了数据和行为,构成了应用程序的基本构建块。然而,在Java语言中,每当使用new关键字或其他方式创建一个新对象时,背后发生了什么?这个问题的答案隐藏在JV…...
使用开源在线聊天工具Fiora轻松搭建个性化聊天平台在线交流
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:人工智能教程 文章目录 前言1.关于Fiora2.安装Docker3.本地部署Fiora4.使用Fiora5.cpolar内网穿透工具安装6.创建远程连接公网地址7.固定Uptime …...
ffmpeg之显示一个yuv照片
显示YUV图片的步骤 1.初始化SDL库 目的:确保SDL库正确初始化,以便可以使用其窗口、渲染和事件处理功能。操作:调用 SDL_Init(SDL_INIT_VIDEO) 来初始化SDL的视频子系统。 2.创建窗口用于显示YUV图像: 目的:创建一个…...
MySQL中Performance Schema库的详解(下)
昨天说了关于SQL语句相关的,今天来说说性能相关的,如果没有看过上篇请点传送门https://blog.csdn.net/2301_80479959/article/details/144693574?fromshareblogdetail&sharetypeblogdetail&sharerId144693574&sharereferPC&sharesource…...
【Rust自学】7.1. Package、Crate和定义Module
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 7.1.1. Rust的代码组织 代码组织主要包括: 那些细节可以对外暴露,而哪些细节是私有的在作用域内哪些名称有效… …...
【Git】-- 版本说明
Alpha:是内部测试版,一般不向外部发布,会有很多 Bug .一般只有测试人员使用。Beta:也是测试版,这个阶段的版本会一直加入新的功能。在 Alpha 版之后推出。RC:(Release Candidate) 顾名思义么 ! 用在软件上就是候选版本。系统平台…...
1919C. Grouping Increases
问题描述 序列 X X X,划分成两个字序列 A , B A,B A,B,其中惩罚是 A , B A,B A,B之中, A [ i ] < A [ i 1 ] , B [ i ] < B [ i 1 ] A[i] < A[i1], B[i] < B[i1] A[i]<A[i1],B[i]<B[i1]的个数 思路 拆分 X X X…...
Pion WebRTC 项目教程
Pion WebRTC 项目教程 webrtc Pure Go implementation of the WebRTC API [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/we/webrtc 1. 项目目录结构及介绍 Pion WebRTC 项目的目录结构如下: pion/webrtc ├── api ├── examples ├── inter…...
【安全编码】Web平台如何设计防止重放攻击
我们先来做一道关于防重放的题,答案在文末 防止重放攻击最有效的方法是( )。 A.对用户密码进行加密存储使用 B.使用一次一密的加密方式 C.强制用户经常修改用户密码 D.强制用户设置复杂度高的密码 如果这道题目自己拿不准,或者…...
VUE3+django接口自动化部署平台部署说明文档(使用说明,需要私信)
网址连接:http://118.25.110.213:5200/#/login 账号/密码:renxiaoyong 1、VUE3部署本地。 1.1本地安装部署node.js 1.2安装vue脚手架 npm install -g vue/cli # 或者 yarn global add vue/cli1.3创建本地项目 vue create my-vue-project1.4安装依赖和插…...
Python爬虫(入门+进阶)
简介 围绕 Python 爬虫展开,包括四个章节。第一章从 Python 爬虫入门,涵盖爬虫概念、Requests 爬取、Xpath 解析、数据保存及入库等知识,并结合知乎、豆瓣、淘宝等案例讲解浏览器抓包及 Selenium 爬取动态网页。第二章介绍 Scrapy 框架&…...
保姆级教程Docker部署RabbitMQ镜像
目录 1、安装Docker及可视化工具 2、创建挂载目录 3、运行RabbitMQ容器 4、Compose运行RabbitMQ容器 5、开启界面插件 6、查看RabbitMQ运行状态 7、常见问题处理 1、安装Docker及可视化工具 Docker及可视化工具的安装可参考:Ubuntu上安装 Docker及可视化管理…...
【RAII | 设计模式】C++智能指针,内存管理与设计模式
前言 nav2系列教材,yolov11部署,系统迁移教程我会放到年后一起更新,最近年末手头事情多,还请大家多多谅解。 上一节我们讲述了C移动语义相关的知识,本期我们来看看C中常用的几种智能指针,并看看他们在设计模式中的运…...
Linux复习3——管理文件系统2
修改文件权限命令 chmod 功能: chmod 命令主要用于修改文件或者目录的权限 只有文件所有者和超级用户可以修改文件或目录的权限 (1)使用数字表示法修改权限 所谓数字表示法是指将读取(r)、写入(w)和执行(x)分别以4、2、1来表示,没有授予的部分就表示…...
c++---------数据类型
基本数据类型 整数类型(Integral Types) int(整型) 这是最常用的整数类型,通常用于存储一般范围的整数值。在32位系统中,int类型一般占用4个字节,取值范围大约是 - 2147483648到2147483647。例如…...
前端Python应用指南(三)Django vs Flask:哪种框架适合构建你的下一个Web应用?
《写给前端的python应用指南》系列: (一)快速构建 Web 服务器 - Flask vs Node.js 对比(二)深入Flask:理解Flask的应用结构与模块化设计 在上一篇博文中,我们深入探讨了Flask框架,…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
