C++设计模式-单例模式,反汇编

文章目录

  • 25. 单例模式
    • 25.1. 饿汉式单例模式
    • 25.2. 懒汉式单例模式
      • 25.2.1. 解决方案1
      • 25.2.2. 解决方案2 (推荐写法)

运行在VS2022,x86,Debug下。

25. 单例模式

  • 单例即该类只能有一个实例。

  • 应用:如在游戏开发中,可以使用单例模式来管理各种资源,确保这些资源在整个游戏中只被加载和管理一次。

  • 实现

    • 将构造函数和析构函数私有,不允许外部构造或析构类对象。
    • 将拷贝构造函数、赋值运算符、移动构造函数和移动赋值运算符删除,不允许复制或移动类对象。
    • 需要有一个静态函数接口,返回唯一的静态实例。
  • 分类

    • 饿汉式单例模式:在main()开始前,实例就已经存在了。
    • 懒汉式单例模式:在第一次调用获取实例时才创建实例。

25.1. 饿汉式单例模式

  • 代码如下。
class Singleton
{
private:Singleton() {} //私有构造函数~Singleton() {} //私有析构函数Singleton(const Singleton&) = delete; //删除拷贝构造函数Singleton& operator=(const Singleton&) = delete; //删除赋值运算符Singleton(Singleton&&) = delete; //删除移动构造函数Singleton & operator=(Singleton&&) = delete; //删除移动赋值运算符private:static Singleton instance; //静态成员变量,存储实例public:static Singleton* getInstance() //静态成员函数,获取实例{return &instance;}
};Singleton Singleton::instance; //静态成员变量实例化int main()
{Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();return 0;
}
  • 在main()处设置断点,监视窗口如下,实例的内存地址为0x00E0C138。

在这里插入图片描述

  • 总结
    • 优点:线程安全,因为程序运行时就已经生成唯一的实例。
    • 缺点:不是按需创建实例。

25.2. 懒汉式单例模式

  • 新增一个静态函数接口,用于释放实例内存。
  • 代码如下。
class Singleton
{
private:Singleton(){} //私有构造函数~Singleton(){} //私有析构函数Singleton(const Singleton&) = delete; //删除拷贝构造函数Singleton& operator=(const Singleton&) = delete; //删除赋值运算符Singleton(Singleton&&) = delete; //删除移动构造函数Singleton & operator=(Singleton&&) = delete; //删除移动赋值运算符private:static Singleton* instance; //静态成员变量,存储实例public:static Singleton* getInstance() //静态成员函数,获取实例{if (instance == nullptr)instance = new Singleton();return instance;}static void deleteInstance() //静态成员函数,释放实例{if (instance != nullptr){delete instance;instance = nullptr;}}
};Singleton* Singleton::instance = nullptr; //静态成员变量定义和初始化int main() 
{Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();Singleton::deleteInstance();return 0;
}
  • 在main()处设置断点,监视窗口如下,此时未生成实例。

在这里插入图片描述

  • 第一次调用getInstance()获取实例时,生成实例,实例的内存地址为0x00E16A08。

在这里插入图片描述

  • 总结
    • 优点:按需创建实例。
    • 缺点:
      • 不是线程安全,如两个线程同时调用getInstance()获取实例,同时运行到判断instance是否为nullptr的if语句,并且instance并未创建,那么两个线程都会创建一个实例。
      • 内存释放问题,在程序执行结束时,调用deleteInstance()释放实例内存,但是要确保delete之后,没有代码再调用getInstance()或者访问已释放的内存,存在安全隐患。

25.2.1. 解决方案1

  • 针对线程安全问题,加锁。
  • 针对内存释放问题,对于全局变量或静态变量,main()返回后会调用析构函数。基于此,可以定义一个静态成员变量,用于释放实例内存。
  • 代码如下。
mutex m;class Singleton
{
private:Singleton() {} //私有构造函数~Singleton() {} //私有析构函数Singleton(const Singleton&) = delete; //删除拷贝构造函数Singleton& operator=(const Singleton&) = delete; //删除赋值运算符Singleton(Singleton&&) = delete; //删除移动构造函数Singleton& operator=(Singleton&&) = delete; //删除移动赋值运算符static Singleton* instance; //静态成员变量,存储实例class Garbo //在析构函数中释放实例{public:~Garbo(){if (Singleton::instance != nullptr){delete instance;instance = nullptr;}}};static Garbo garbo; //静态成员变量,在程序执行结束时,系统会调用它的析构函数public:static Singleton* getInstance() //静态成员函数,获取实例{if (instance == nullptr) //加锁前判断,这样如果实例存在,就不需加锁了{lock_guard<mutex>lock(m); //创建实例前加锁if (instance == nullptr)instance = new Singleton();}return instance;}
};Singleton* Singleton::instance = nullptr; //静态成员变量定义和初始化int main()
{Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();return 0;
}

25.2.2. 解决方案2 (推荐写法)

  • 在静态函数接口里,定义局部静态变量,存储实例。那么它会在第一次调用获取实例时才创建,可以按需创建实例。同时内部__Init_thread_header()和_Init_thread_footer()可以保证局部静态变量的初始化是线程安全的。
  • 代码如下。
class Singleton
{
private:Singleton() {} //私有构造函数~Singleton() {} //私有析构函数Singleton(const Singleton&) = delete; //删除拷贝构造函数Singleton& operator=(const Singleton&) = delete; //删除赋值运算符Singleton(Singleton&&) = delete; //删除移动构造函数Singleton& operator=(Singleton&&) = delete; //删除移动赋值运算符public:static Singleton* getInstance() //静态成员函数,获取实例{static Singleton instance; //局部静态成员变量,存储实例return &instance;}
};int main()
{Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();return 0;
}
  • 反汇编分析如下。

在这里插入图片描述

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

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

相关文章

搭建大型分布式服务(三十九)SpringBoot 整合多个kafka数据源-支持Aware模式

系列文章目录 文章目录 系列文章目录前言一、本文要点二、开发环境三、原项目四、修改项目五、测试一下五、小结 前言 本插件稳定运行上百个kafka项目&#xff0c;每天处理上亿级的数据的精简小插件&#xff0c;快速上手。 <dependency><groupId>io.github.vipjo…

算法第三天力扣第69题:X的平方根

69. x 的平方根 (可点击下面链接或复制网址进行做题) https://leetcode.cn/problems/sqrtx/https://leetcode.cn/problems/sqrtx/ 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被…

SpringBoot的第二大核心AOP系统梳理

目录 1 事务管理 1.1 事务 1.2 Transactional注解 1.2.1 rollbackFor 1.2.2 propagation 2 AOP 基础 2.1 AOP入门 2.2 AOP核心概念 3. AOP进阶 3.1 通知类型 3.2 通知顺序 3.3 切入点表达式 execution切入点表达式 annotion注解 3.4 连接点 1 事务管理 1.1 事务…

【踏雪无痕的痕六】——数学中有意思的问题

一、背景介绍 提出一个问题往往比解决一个问题更有意义&#xff0c;因为提出一个问题相当于提出了一个思考问题的维度&#xff1b;而解决一个问题是沿着这个维度将已有的知识串起来的过程 三、过程 1.数人数你会吗&#xff1f; 小名再第10位&#xff0c;小李再第15位&#…

【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

位运算算法

位运算是计算机中常用的一种运算方法&#xff0c;它直接对二进制数的位进行操作。位运算主要包括按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、按位取反&#xff08;~&#xff09;、左移&#xff08;<<&a…

【云原生】Kubernetes----Ingress对外服务

目录 引言 一、K8S对外方式 &#xff08;一&#xff09;NodePort 1.作用 2.弊端 3.示例 &#xff08;二&#xff09;externalIPs 1.作用 2.弊端 3.示例 &#xff08;三&#xff09;LoadBalancer 1.作用 2.弊端 &#xff08;四&#xff09;Ingress 二、Ingress的…

ChatGPT-4o抢先体验

速度很快&#xff0c;结果很智能&#xff0c;支持多模态输入输出&#xff0c;感兴趣联系作者。 windows/linux/mac 客户端下载参考&#xff1a;https://github.com/lencx/Noi

dns域名解析服务和bond网卡

目录 dns域名解析服务 一、DNS 1、定义 2、以www.baidu.com为例 3、域名体系结构 4、DNS解析使用的协议和端口 5、dns域名解析的过程 6、dns解析的优先级 二、如何实现域名解析 1、域名解析 2、bind配置文件位置 &#xff08;一&#xff09;正向解析 &#xff08;…

IP协议1.0

基本概念&#xff1a; • 主机: 配有IP地址, 但是不进⾏路由控制的设备; • 路由器: 即配有IP地址, ⼜能进⾏路由控制; • 节点: 主机和路由器的统称; IP协议的报头 • 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4. • 4位头部⻓度(header length): IP头部的⻓…

高并发系统限流原理

短时间内巨大的访问流量&#xff0c;我们如何让系统在处理高并发的同时还能保证自身系统的稳定性&#xff1f;估计有人会说&#xff0c;增加机器就可以了&#xff0c;因为我的系统架构设计就是按照分布式思想进行架构设计的&#xff0c;所以可以只需要增加机器就可以解决问题了…

计算机组成原理·考点知识点整理

根据往年考试题&#xff0c;对考点和知识点的一个整理。 校验编码 码距 一种编码的最小码距&#xff0c;其实就是指这种编码的码距。码距有两种定义&#xff1a; 码距所描述的对象含义 2 2 2 个特定的码其二进制表示中不同位的个数一种编码这种编码中任意 2 2 2 个合法编码的…

QT+FFmpeg+Windows开发环境搭建(加薪点)

01、Windows 环境搭建 FFMPEG官网:http://ffmpeg.org/ 02、下载4.2.1版本源码 源码:https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2 03、下载4.2.1编译好的文件 下载已经编译好的FFMPEG)(迅雷下载很快) 网址:https://ffmpeg.zeranoe.com/builds/ 32位下载地址:(迅雷…

【wiki知识库】05.分类管理模块--后端SpringBoot模块

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 一、&#x1f525;今日目标 二、☀SpringBoot代码修改 1.使用逆向工程生成Category表结构 2. 新增CategoryQueryParam 3.新增CategorySaveParam 4.新增CategotyQueryVo 三、&#x1f916;新增分类管理的相关接口…

8. C#多线程基础概念

文章目录 一. 目标二. 技能介绍① 进程和线程② 为什么需要多线程③ C#实现多线程的方式④ 线程的操作(创建_终止_挂起_恢复) 一. 目标 进程和线程基本概念为什么需要多线程?C#实现多线程的方式?线程Thread的创建,终止,挂起和恢复? 二. 技能介绍 ① 进程和线程 什么是进程…

IO流,文件操作

参考 Java IO 基础知识总结 | JavaGuide 史上最骚最全最详细的IO流教程&#xff0c;没有之一&#xff01; - 宜春 - 博客园 零、io-流简介 IO 即 Input/Output&#xff0c;输入和输出。数据输入到计算机内存的过程即输入&#xff0c;反之输出到外部存储&#xff08;比如数据…

Microsoft Outlook Lite 引入短信功能

随着科技的不断进步&#xff0c;我们的沟通方式也在不断演变。微软最新推出的 Outlook Lite 应用&#xff0c;不仅为我们提供了一个轻量级的电子邮件管理工具&#xff0c;现在更是带来了一项令人兴奋的新功能——短信服务。 Outlook Lite&#xff1a;轻量级&#xff0c;功能全…

手眼标定学习笔记

目录 标定代码&#xff1a; 手眼标定原理学习 什么是手眼标定 手眼标定的目的 eye in hand eye to hand AXXB问题的求解 标定代码&#xff1a; GitHub - pumpkin-ws/HandEyeCalib 推荐博文&#xff1a; https://zhuanlan.zhihu.com/p/486592374 手眼标定原理学习 参…

带DSP音效处理D类数字功放TAS5805M中文资料

国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …

Vue3中的常见组件通信之v-model

Vue3中的常见组件通信之v-model 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $r…