单例模式
文章目录
- 单例模式
- 特殊类的设计
- 单例模式
- 饿汉模式
- 懒汉模式
- 懒汉VS饿汉
- 懒汉的线程安全
- 单例对象的释放
单例模式
认识单例模式之前先认识一下几种常见的特殊类的设计。
特殊类的设计
-
设计一个类 只能再堆上创建对象
-
只能再堆上创建,则通过new来创建对象。
- 将类的构造函数设为私有,以防止外部直接创建对象。
- 提供一个静态成员函数,用于在堆上创建对象,并返回指向该对象的指针
- 在类的析构函数中释放对象的内存,以确保对象在不再需要时被正确销毁。
class HeapOnly {public:// 获取堆上对象的静态成员函数static HeapOnly* CreateObj() {return new HeapOnly();}// 删除拷贝构造函数和赋值运算符,以确保只能通过 create() 创建对象HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;private:// 将构造函数设为私有,以防止外部直接创建对象HeapOnly() {// 可以在这里进行初始化操作}// 在析构函数中释放对象的内存~HeapOnly() {// 可以在这里进行资源释放操作}};-
HeapOnly类的构造函数是私有的,外部无法直接调用。 -
向外提供的
CreateObj方法必须定义为static成员函数。因为外部调用该接口就是为了获取对象的,而非静态成员函数必须通过对象才能调用,静态成员函数直接使用类名::函数名即可调用。 -
将拷贝构造函数和赋值运算符重载函数之间禁用,也可以设置为私有属性,以防通过拷贝和赋值创建对象。
-
使用示例:
int main() {// 创建堆上的对象HeapOnly* obj = HeapOnly::create();// 使用对象...// 删除对象delete obj;return 0; }
-
-
设计一个类 只能再栈上创建对象
-
只能再栈上创建对象,说明不能通过new创建对象,也不能定义为static对象。创建方式如下:
- 将类的构造函数设为私有,防止外部直接调用构造函数在堆上创建对象。
- 在类中定义一个静态成员函数,用于创建对象,并返回该对象的引用或指针。
class StackOnly { public:// 获取栈上对象的静态成员函数static StackOnly& create() {// 在静态成员函数中创建对象,返回对象的引用static StackOnly instance;return instance;}// 删除拷贝构造函数和赋值运算符,以防止通过拷贝或赋值创建对象StackOnly(const StackOnly&) = delete;StackOnly& operator=(const StackOnly&) = delete;private:// 将构造函数设为私有,防止外部直接创建对象StackOnly() {// 可以在这里进行初始化操作}}; int main() {// 创建栈上的对象StackOnly& obj = StackOnly::create();return 0; } -
将
StackOnly类的构造函数私有化,外部无法直接调用。限制了通过new创建对象和static对象。 -
静态局部对象的引用是存储在栈上的。
-
将拷贝构造函数和赋值运算符重载函数之间禁用,也可以设置为私有属性,以防通过拷贝和赋值创建对象。
-
-
设计一个类 不能被拷贝
- 将拷贝构造函数和赋值运算符设置为私有,或者直接使用delete禁用即可。
class NonCopyable { public:NonCopyable() {} // 默认构造函数// 禁用拷贝构造函数和拷贝赋值运算符NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete; };int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 这行会导致编译错误,因为拷贝构造函数被删除了// NonCopyable obj3;// obj3 = obj1; // 这行会导致编译错误,因为拷贝赋值运算符被删除了return 0; } -
设计一个类 不能被继承
-
将该类的构造函数设置为私有即可,因为子类对象构造时,必须先调用父类构造,构造父类那一部分成员。将其私有化后,子类对象创建对象时,无法调用父类构造函数进行初始化。
class NonInherit { public:static NonInherit CreateObj(){return NonInherit();} private://将构造函数设置为私有NonInherit(){} }; -
也可以使用C++11提供的关键字
final。别final修饰的类,无法被继承class NonInherit final {//... };
-
-
设计一个类 只能创建一个对象
- 一个类只能创建一个对象,将其称之为单例模式。
单例模式
单例模式是一种设计模式(Design Pattern),设计模式就是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的就是为了可重用代码、让代码更容易被他人理解、保证代码可靠性程序的重用性。
单例模式指的就是一个类只能创建一个对象,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
单例模式有两种实现方式,分别是饿汉模式和懒汉模式
饿汉模式
单例模式的实现方式:
- 私有化构造函数:确保其他类无法直接实例化该类,只能通过指定的方法获取类的实例。
- 私有静态成员变量:保存类的唯一对象。并在程序入口之前完成单例对象的初始化。
- 公有静态方法:提供对唯一实例的访问。
class Singleton
{
public: //公有静态方法static Singleton* GetInstace(){return _inst;}//禁用拷贝构造和赋值运算符Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton) = delete;
private://私有化构造函数Singleton(){}//私有静态成员变量static Singleton* _inst;
};
Singleton* Singleton::_inst = new Singleton;//静态成员变量的初始化int main()
{//获取单例对象Singleton* ojb = Singleton::GetInstace();
}
懒汉模式
懒汉模式的实现方式:
- 私有化构造函数:确保其他类无法直接实例化该类,只能通过指定的方法获取类的实例。
- 私有静态成员变量:保存类的唯一对象,将其初始化为空。
- 公有静态方法:提供对唯一实例的访问。如果实例不存在,则创建一个新实例并返回;如果实例已存在,则返回现有实例
// 懒汉模式
class Singleton
{
public://公有静态方法static Singleton* GetInstance(){if (_inst == nullptr){_inst = new Singleton;}return _inst;}//禁用拷贝构造和赋值运算符Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
private://私有构造函数Singleton(){}//私有静态成员变量static Singleton* _inst;
};
Singleton* Singleton::_inst = nullptr;int main()
{//获取单例对象Singleton* obj = Singleton::GetInstance();return 0;
}
懒汉VS饿汉
- 饿汉
- 在类加载时就创建实例。无论是否使用该实例,都会在类加载时被创建。如果这个类很大,会影响程序的启动速度。
- 在mian程序加载之前就会被实例化,不存在线程安全问题
- 使用简单
- 懒汉
- 在需要时才创建实例。也就是说,实例的创建延迟到第一次使用时才发生。
- 在多线程环境下需要考虑线程安全性,特别是在第一次获取实例时,可能会存在多个线程同时创建实例的问题。需要通过加锁或者双重检查锁定等技术来确保线程安全性。
- 使用复杂
懒汉的线程安全
上面写的懒汉单例并不是线程安全的,在多线程场景下,存在线程安全的问题。GetInstance函数第一次调用时,需要对_inst进行写入操作,这个操作不是线程安全的,多个线程可能同时调用GetInstance进行写入,如果不对此过程进行加锁保护,多线程下就会各自创建出一个对象。
对于饿汉模式来讲,不存在线程安全的问题,因为在main函数之前就已经创建好对象了。
在GetInstance中创建单例对象进行加锁保护。
static Singleton* GetInstance()
{if (_inst == nullptr){_mtx.lock();if (_inst == nullptr){_inst = new Singleton;}_mtx.unlock();}return _inst;
}
进行双重检查加锁来保证线程安全。双重检查会提高效率,如果是单检查,如下:
static Singleton* GetInstance()
{_mtx.lock();if (_inst == nullptr){_inst = new Singleton;}_mtx.unlock();return _inst;
}
这样每次访问单例对象时都要进行加锁,加锁之后才能进行判断。双检查之后,只有第一次访问单例对象时才需要加锁。后续访问时无需加锁了。提高了效率。
单例对象的释放
单例对象创建后一般在整个程序运行期间都可能会使用,所以我们可以不考虑单例对象的释放,程序正常结束时会自动将资源归还给操作系统。
有些场景下可能需要提前释放单例对象,可以参考一下方式:
- 提供一个公有的
DelInstance函数,在该函数中进行单例对象释放的操作,当不需要单例对象时,调用此函数进行单例对象的释放。函数如下:
static void DelInstance()
{_mtx.lock();if (_inst != nullptr){delete _inst;_inst = nullptr;}_mtx.unlock();
}
释放操作只能调用一次,单检查加锁足够了,无需双检查加锁。
注意:单例模式中的单例对象和互斥锁,和提供的公有方法都是static的。
首先要明确static修饰成员变量和成员函数的特征:
-
静态成员变量:
- 所有类的对象共享静态成员变量的值。
- 在类声明中声明为静态成员变量,但在类外部必须在类外进行定义和初始化。
- 静态成员变量的内存只分配一次,直到程序结束才会被释放。
- 可以在类外部通过类名和作用域解析运算符(::)来访问静态成员变量。
-
静态成员函数:
- 不与任何特定的对象相关联,可以直接通过类名调用。
- 静态成员函数没有隐含的this指针。
- 静态成员函数不能访问非静态成员变量和非静态成员函数(除非它们是同一个类中的静态成员)。
- 由于它们不与特定对象相关联,因此无法在静态成员函数中使用this指针。
在单例模式中,需要将实例保存在静态成员变量中,是因为:
- 全局可访问性:静态成员变量属于类而不是对象,因此可以被该类的所有对象访问。这符合单例模式的要求,即只有一个实例,并且可以在任何地方访问到该实例。
- 生命周期与类相同:静态成员变量随着类的加载而初始化,而不是随着对象的创建而初始化。这意味着无论是否存在对象,静态成员变量都会在类加载时创建,从而保证了在整个应用程序生命周期内只有一个实例存在。
因此,将单例实例保存在静态成员变量中能够满足单例模式的要求,确保了实例的全局可访问性和唯一性。
相关文章:
单例模式
文章目录 单例模式特殊类的设计单例模式饿汉模式懒汉模式懒汉VS饿汉懒汉的线程安全单例对象的释放 单例模式 认识单例模式之前先认识一下几种常见的特殊类的设计。 特殊类的设计 设计一个类 只能再堆上创建对象 只能再堆上创建,则通过new来创建对象。 将类的构造函…...
Android OpenMAX - 开篇
Android Media是一块非常庞大的内容,上到APP的书写,中到播放器的实现、封装格式的了解,下到OMX IL层的实现、Decoder的封装,每一块都需要我们下很大的功夫学习。除此之外,我们还要对一些相关的模块进行了解,…...
ubuntu开启ssh服务
1.安装openssh-server sudo apt-get install openssh-server 2.开启服务 sudo servive ssh start 3.设置开机自启动 sudo systemctl enable ssh 4.检查是否开启成功 ps -ef | grep ssh 如使用xshell等连接时失败,可以尝试关闭防火墙: sudo sys…...
测试缺陷定位的基本方法
前后端bug特征 后端: 业务逻辑问题:如任务状态未扭转成功,创建任务失败等数据类问题:如新增的任务在页面没有展示出来等性能类问题:提交任务一直显示创建中、批量操作等待耗时长超时等 前端: 页面显示类…...
【数字图像处理matlab系列】数组索引
【数字图像处理matlab系列】数组索引 【先赞后看养成习惯】【求点赞+关注+收藏】 MATLAB 支持大量功能强大的索引方案,这些索引方案不仅简化了数组操作,而且提高了程序的运行效率。 1. 向量索引 维数为1xN的数组称为行向量。行向量中元素的存取是使用一维索引进行的。因此…...
【2024系统架构设计】案例分析- 3 数据库
目录 一 基础知识 二 真题 一 基础知识 1 ORM ORM(Object—Relationl Mapping...
vue基础——java程序员版(总集)
前言: 这是一个java程序员的vue学习记录。 vue是前端的主流框架,按照如今的就业形式作为后端开发的java程序员也是要有所了解的,下面是本人的vue学习记录,包括vue2的基本使用以及引入element-ui,使用的开发工具…...
Rancher(v2.6.3)——Rancher配置Harbor镜像仓库
Rancher配置Harbor镜像仓库详细说明文档:https://gitee.com/WilliamWangmy/snail-knowledge/blob/master/Rancher/Rancher%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3.md#8rancher%E9%85%8D%E7%BD%AEharbor ps:如果觉得作者写的还行,能够满足您的需…...
C++类和对象、面向对象编程 (OOP)
文章目录 一、封装1.抽象、封装2.类和对象(0)学习视频(1)类的构成(2)三种访问权限(3)struct和class的区别(4)私有的成员变量、共有的成员函数(5)类内可以直接访问私有成员,不需要经过对象 二、继承三、多态1.概念2.多态的满足条件3.多态的使用条件4.多态原理剖析5.纯…...
Introduction to Data Mining 数据挖掘
Why Data Mining? • The Explosive Growth of Data: from terabytes to petabytes — Data collection and data availability ◦ Automated data collection tools, database systems, Web, computerized society — Major sources of abundant data ◦ Business: Web, e-co…...
常用的 Git 命令
初始化一个新的仓库: git init 克隆一个仓库: git clone <仓库地址> 查看文件状态: git status 添加文件到暂存区: git add <文件名> 提交文件到仓库: git commit -m "提交说明" 查看提交历…...
jackson:JSON字符串(String)类型的成员序列化和反序列化
对于如下类型定义TestTaskInfo,props字段为JSON字符串(这在数据库经常用到),可以自由保存各种类型的数据 Data public class TestTaskInfo {private String id;private String props;public TestTaskInfo() {}public TestTaskInfo(String id, String props) {super…...
使用docker的好处???(docker的优势)
标准化环境: Docker通过容器技术封装应用程序及其依赖(如库、配置文件、运行时环境等),确保应用程序在任何环境中都能以一致的方式运行。这种标准化消除了“在我机器上能运行”的问题,因为容器化应用能在开发、测试、生…...
Google AI 肺癌筛查的计算机辅助诊断
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
【字符串算法题记录】反转字符串中的单词(leetcode),右旋字符串(kama)——双指针以及反转的奇思妙用
反转字符串中的单词 题目链接 思考 这题的思路顺序是:移除多余空格(双指针法)——》反转整个字符串)——》反转字符串中每个单词。 移除多余空格(双指针法) 因为字符串开头也可能有多个字符࿰…...
基于springboot+vue调用百度ai实现车牌号识别功能
百度车牌号识别官方文档 结果视频演示 后端代码 private String getCarNumber(String imagePath, int count) {// 请求urlString url "https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate";try {byte[] imgData FileUtil.readFileByBytes(imagePath);Stri…...
【NTN 卫星通信】 TN和多NTN配合的应用场景
1 场景描述 此场景描述了农村环境,其中MNO (运营商TerrA)仅在城市附近提供本地地面覆盖,而MNO (SatA)提供广泛的NTN覆盖。SatA使用GSO轨道和NGSO轨道上的卫星。SatA与TerrA有漫游协议,允许: 所有TerrA用户的连接,当这些用户不…...
健康餐饮必备!油烟净化器超强洁净餐饮环境
我最近分析了餐饮市场的油烟净化器等产品报告,解决了餐饮业厨房油腻的难题,更加方便了在餐饮业和商业场所有需求的小伙伴们。 在如今注重健康生活的时代,餐饮业不仅需要美味佳肴,更需要一个清洁、舒适的用餐环境。油烟净化器作…...
Redis修改开源协议,6大备胎重见天日
背景:Redis2018年以来修改了多次开源协议,以前是把一些高级功能收费,这次彻底怒了,把核心代码的协议修改为RSALv2和SSPL双重协议,这个修改对普通用户不受影响,是向所有云厂商开炮,以后云厂商将不…...
使用python读取csv文件快速插入postgres数据库
使用python读取csv文件快速插入postgres数据库 下面为完整代码 import pandas as pd import cStringIO import warnings from sqlalchemy import create_engine import sys reload(sys) sys.setdefaultencoding(utf8) warnings.filterwarnings(ignore) engine create…...
VIVE Focus3 Unity开发避坑指南:JDK11.0.22与Wave SDK 4.2集成要点
1. 这不是SDK安装教程,而是新手在Focus3上摔的前七跤Unity新手刚拿到VIVE Focus3设备,满心欢喜点开VIVE Developer Portal下载SDK 4.2,解压、导入、Build、Run——然后卡在黑屏、报错、手势没反应、手柄漂移、甚至Unity编辑器直接崩溃。我带过…...
用AI 30分钟搞一个Todo应用?这事到底靠不靠谱
用AI 30分钟搞一个Todo应用?这事到底靠不靠谱 先说结论AI辅助生成代码骨架确实能缩短初始搭建时间,但调试、联调、部署环节的效率提升远不如宣传的20倍。这个流程更适合原型验证和个人小工具,不适合需要长期维护、协作或复杂业务逻辑的项目。…...
关于我尝试写博客这档事
一、起因 在学习过数据结构后,希望更改目前记笔记的形式,于是想到整理成文章,通过开源方式锻炼表达力与技术理解力,希望复习与拓展所学习过的知识,使用费曼学习法学习 二、自我介绍 1.基本信息 博主名为Doubletful(Dou…...
基于Python + LLM的AI客服协作系统设计与实现
🧑💻 博主介绍 & 诚邀关注 作者:专注于 Java、Python、前端开发的技术博主 | 全网粉丝 30 万 在校期间协助导师完成毕业设计课题分类、论文格式初审及代码整理工作;工作后持续分享毕设思路,助力毕业生顺利完成…...
不只是打驱动:深入解读Intel Arc显卡在Linux下的RBAR技术及其对AI性能的实际影响
深入解析Intel Arc显卡RBAR技术:Linux环境下的AI性能优化实践 当一块Intel Arc显卡插入Linux工作站时,大多数用户的第一反应是寻找驱动安装指南。但真正影响AI推理性能的关键,往往隐藏在PCIe总线的一个名为RBAR(Resizable Base Ad…...
分布式/集群/微服务
分布式:将一个系统划分为多个子系统,每个子系统在不同的服务器上运行,并通过网络通信进行协作集群:一组相互独立的计算机系统协同工作,共同提供服务或处理任务,它们之间可以共享资源和负载均衡微服务&#…...
仅限内部技术团队流通:OpenAI官方未公开的API调试技巧——12个隐藏Header与调试模式启用密钥
更多请点击: https://kaifayun.com 第一章:ChatGPT API调用方法 调用 ChatGPT API 需通过 OpenAI 提供的 RESTful 接口,使用 HTTPS 请求向 https://api.openai.com/v1/chat/completions 端点发送 JSON 格式的 POST 请求。核心依赖包括有效的…...
观察使用Taotoken后月度AI模型API账单的清晰度与成本分布
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 观察使用Taotoken后月度AI模型API账单的清晰度与成本分布 作为个人开发者或技术团队的负责人,在项目开发中引入多个大模…...
百度网盘提取码智能查询工具:3分钟掌握资源密码自动获取技巧
百度网盘提取码智能查询工具:3分钟掌握资源密码自动获取技巧 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘分享链接的提取码而烦恼吗?每次找到心仪的资源却卡在密码输入环节࿰…...
终极跨平台3D资产迁移革命:DazToBlender插件完整指南
终极跨平台3D资产迁移革命:DazToBlender插件完整指南 【免费下载链接】DazToBlender Daz to Blender Bridge 项目地址: https://gitcode.com/gh_mirrors/da/DazToBlender 你是否曾经在Daz Studio中精心创作了一个完美的3D角色,却因为无法在Blende…...
