C++设计模式创建型之单例模式
一、概述
单例模式也称单态模式,是一种创建型模式,用于创建只能产生一个对象实例的类。例如,项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等,甚至如果吧整个Windows操作系统看成一个项目,那么其中只存在一个任务管理器窗口等。引入单例模式的实现意图:保证一个类仅有一个实例存在,同时提供能对该实例访问的全局方法。
二、单例模式分类
1、懒汉模式
1)代码示例
class CSingletonImpl
{
public:
static CSingletonImpl* GetInstance()
{
if (m_pInstance == nullptr)
{
m_pInstance = new CSingletonImpl;
}
return m_pInstance;
}
private:
CSingletonImpl(){};
~CSingletonImpl(){};
CSingletonImpl(const CSingletonImpl& the);
CSingletonImpl& operator=(const CSingletonImpl& other);
private:
static CSingletonImpl* m_pInstance;
};
CSingletonImpl*CSingletonImpl::m_pInstance = nullptr;
2)说明
单例模式为了防止多对象问题,将构造函数,析构函数,拷贝构造函数,赋值运算符函数设置为私有,同时设置公有唯一接口方法来创建对象,同时定义类静态指针。这是通用方法,那么会有什么问题呢?如果在单一线程中使用则没什么问题,但是在多线程中使用则可能导致问题,如果多个线程可能会因为操作系统时间片调度问题切换造成多对象产生,那么解决这个问题的方案就是对GetInstance()成员函数枷锁。
示例代码:
加入私有成员变量:static std::mutex m_mutex;
static CSingletonImpl* GetInstance()
{
m_mutex.lock();
if (m_pInstance == nullptr)
{
m_pInstance = new CSingletonImpl;
}
m_mutex.unlock();
return m_pInstance;
}
加入以上代码没有问题了吗?呵呵,还不行,虽然对接口函数加锁,从代码逻辑上没有问题,实现了线程安全,但是从执行效率上来说,是有大问题的。当程序运行中GetInstance()可能会被多个线程频繁调用,每次调用都会经历加锁解锁的过程,这样的话会严重影响程序执行效率,而且加锁机制仅仅对第一次创建对象有意义,对象一旦创建则变成只读对象,在多线程中,对只读对象的访问加锁不仅代价大,而且无意义。那么如何解决这个问题呢?那就是双重锁定机制,基于这种机制函数实现代码:
static CSingletonImpl* GetInstance()
{
if (m_pInstance == nullptr)
{
std::lock_guard<std::mutex> siguard(si_mutex);
if (m_pInstance == nullptr)
{
m_pInstance = new CSingletonImpl;
}
}
return m_pInstance;
}
上述双重锁定机制看起来比较完美,但实际上存在潜在的问题,内存访问重新排序导致双重锁定失效的问题,比较推荐的方法时C++11新标准的一些特性,示例代码如下:
#include <mutex>
#include <atomic>
//通过原子变量解决双重锁定底层问题(load,store)
class CSingletonImpl
{
public:
static CSingletonImpl* GetInstance()
{
CSingletonImpl* task = m_taskQ.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (task == nullptr)
{
std::lock_guard<std::m_mutex> lock(m_mutex);
task = m_taskQ.load(std::memory_order_relaxed);
if (task == nullptr)
{
task = new CSingletonImpl;
std::atomic_thread_fence(std::memory_order_release);
m_taskQ.store(task, std::memory_order_relaxed);
}
}
return task;
}
private:
CSingletonImpl(){};
~CSingletonImpl(){};
CSingletonImpl(const CSingletonImpl& the);
CSingletonImpl& operator=(const CSingletonImpl& other);
private:
static std::mutex m_mutex;
static std::atomic<CSingletonImpl*> m_taskQ;
};
std::mutex CSingletonImpl::m_mutex;
std::atomic<CSingletonImpl*> CSingletonImpl::m_taskQ;
2、饿汉模式
1)示例代码
class CSingletonImpl
{
public:
static CSingletonImpl* GetInstance()
{
return m_pInstance;
}
private:
CSingletonImpl(){};
~CSingletonImpl(){};
CSingletonImpl(const CSingletonImpl& the);
CSingletonImpl& operator=(const CSingletonImpl& other);
private:
static CSingletonImpl* m_pInstance;
};
CSingletonImpl*CSingletonImpl::m_pInstance = new CSingletonImpl();
2)说明
此类模式可称为饿汉式--------程序一执行不管是否调用了GetInstance()成员函数,这个单例类对象就已经被创建了。在饿汉式单例类代码的实现必须要注意,如果一个项目中有多个.cpp源文件,而且这些源文件中包含对全局变量的初始化代码,例如某个.cpp中可能存在如下代码:
int g_test = CSingletonImpl::GetInstance()->m_i; //m_i是int类型变量
那么这样的代码是不安全的,因为多个源文件中全局变量的初始化顺序是不确定的,很可能造成GetInstance()函数返回是nullptr,此时去访问m_i成员变量肯定会导致程序执行异常。所以,对饿汉式单例类对象的使用,应该在程序入口函数开始执行后,例如main函数后。
注意:函数第一次执行时被初始化的静态变量与通过编译器常量进行初始化的基本类型静态变量这两种情况,不要再单例类的析构函数中引用其他单例类对象。
相关文章:
C++设计模式创建型之单例模式
一、概述 单例模式也称单态模式,是一种创建型模式,用于创建只能产生一个对象实例的类。例如,项目中只存在一个声音管理系统、一个配置系统、一个文件管理系统、一个日志系统等,甚至如果吧整个Windows操作系统看成一个项目…...
杂记 | 记录一次使用Docker安装gitlab-ce的过程(含配置交换内存)
文章目录 01 准备工作02 (可选)配置交换内存03 编辑docker-compose.yml04 启动并修改配置05 nginx反向代理06 (可选)修改配置文件07 访问并登录 01 准备工作 最近想自建一个gitlab服务来保存自己的项目,于是找到gitla…...
MyBatis@Param注解的用法
一、前言 本人在学习mybatis的过程中遇到的一个让人不爽的bug,在查找了些相关的资料后得以解决,遂记录。 二、报错及解决 mapper中有一方法: Select("select * from emp " "where name like concat(%, #{name}, %) "…...
Shader 编程:GLSL 重要的内置函数
该原创文章首发于微信公众号:字节流动 未经作者(微信ID:Byte-Flow)允许,禁止转载 前面发了一些关于 Shader 编程的文章,有读者反馈太碎片化了,希望这里能整理出来一个系列,方便系统的…...
浏览器同源策略
浏览器同源策略 同源策略:是一个重要的浏览器的安全策略,用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互 它能帮助阻隔恶意文档,减少可能被攻击的媒介 例如:被钓鱼网站收集信息,使用ajax发起…...
GD32F103的EXTI中断和EXTI事件
GD32F103的EXTI可以产生中断,也产生事件信号。 GD32F03的EXTI触发源: 1、I/O管脚的16根线; 2、内部模块的4根线(包括LVD、RTC闹钟、USB唤醒、以太网唤醒)。 通过配置GPIO模块的AFIO_EXTISSx寄存器,所有的GPIO管脚都可以被选作EXTI的触发源…...
了解 spring MVC + 使用spring MVC - springboot
前言 本篇介绍什么是spring MVC ,如何使用spring MVC,了解如何连接客户端与后端,如何从前端获取各种参数;如有错误,请在评论区指正,让我们一起交流,共同进步! 文章目录 前言1. 什么…...
C#中的Invoke
在 C# 中,Invoke() 是一个用于调用方法的方法,它能够在运行时动态地调用一个方法。 Invoke() 方法的使用方式有两种: 通过 MethodInfo 对象调用: using System.Reflection;namespace ConsoleApp_Invoke {public class Program{…...
Hive终端命令行打印很多日志时,如何设置日志级别
示例:use test; 切换到test数据库时,输出很多日志信息不方便看结果,如下图。 解决方法: 退出hive命令行界面(ctrlC)执行“vi /usr/local/apache-hive-3.1.2-bin/conf/log4j.properties”命令,创…...
Android的PopupWindow(详细版)
经典好文推荐,通过阅读本文,您将收获以下知识点: 一、PopupWindow简介 二、PopupWindow 的使用方法 三、底部PopupWindow的实现 四、参考文献 一、PopupWindow简介 在学习PopupWindow之前,我们先了解一下PopupWindow的继承关系。 PopupWindow继承关系如下: java.lang.Obje…...
Navicat远程连接Linux的MySQL
打开Linux终端,进入root权限,用vim打开MySQL的配置文件 vim /etc/mysql/mysql.conf.d/mysqld.cnf将bind-address的值改为0.0.0.0 进入MySQL mysql -u root -p 将root用户改为允许远程登录 update user set host % where user root; 创建用户 CRE…...
Spring IOC
◆ 传统Javaweb开发的困惑 ◆ IoC、DI和AOP思想提出 ◆ Spring框架的诞生 Spring | Home IOC控制反转:BeanFactory 快速入门 package com.xiaolin.service.Impl;import com.xiaolin.dao.UserDao; import com.xiaolin.service.UserService;public class UserServic…...
华为OD机试真题【上班之路】
1、题目描述 【上班之路】 Jungle 生活在美丽的蓝鲸城,大马路都是方方正正,但是每天马路的封闭情况都不一样。 地图由以下元素组成: 1)”.” — 空地,可以达到; 2)”*” — 路障,不可达到; 3&a…...
【linux源码学习】【实验篇】使用bochs运行linux0.11系统(搭建一个自己的工作站)
目录 背景资源获取bochs环境搭建windowsbochs环境搭建linux声明 背景 最近看赵炯老师的《linux内核完全注释》,然后在最后一个习题里面看到使用bochs跑一下0.11的内核代码,本来觉得很难,但是如果做过一遍就会发现其实很简单,这个…...
java+springboot+mysql个人日记管理系统
项目介绍: 使用javaspringbootmysql开发的个人日记管理系统,系统包含超级管理员、管理员、用户角色,功能如下: 超级管理员:管理员管理;用户管理;反馈管理;系统公告;个人…...
旋转图像 LeetCode热题100
题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路 利用矩阵性质,先反转矩阵的每一列元素,再把…...
Vue3 element-plus表单嵌套表格实现动态表单验证
Vue3结合element-plus表单项可以动态添加/删除 部分效果图如下: 另表格有添加和删除按钮,点击提交进行表单验证。 首先data格式必须是对象包裹数组 import { ref, reactive } from vue; import { FormInstance } from element-plus const froms re…...
VSCode插件Todo Tree的使用
在VSCode中安装插件Todo Tree。按下快捷键ctrlshiftP,输入setting.jspn,选择相应的配置范围,我们选择的是用户配置 Open User Settings(JSON),将以下代码插入其中。 //todo-tree 标签配置从这里开始 标签兼容大小写字母(很好的功…...
无人驾驶实战-第五课(动态环境感知与3D检测算法)
激光雷达的分类: 机械式Lidar:TOF、N个独立激光单元、旋转产生360度视场 MEMS式Lidar:不旋转 激光雷达的输出是点云,点云数据特点: 简单:x y z i (i为信号强度) 稀疏:7%&…...
Tomcat 的内存配置
修改 Tomcat 的内存配置,你需要调整 Tomcat 的 Java 虚拟机(JVM)参数。具体来说,你需要修改 catalina.sh(Linux/macOS)或 catalina.bat(Windows)脚本中的 JAVA_OPTS 变量。以下是一般…...
hgproxy4.0.35.0之前版本数据库连接卡在parse状态
文章目录环境症状问题原因解决方案环境 系统平台:Linux x86-64 Red Hat Enterprise Linux 7 版本:4.5.10 症状 查询数据库后台连接进程,发现主备节点均有超过几天的长连接,且状态卡在parse状态 问题原因 数据库会话sql出错后…...
洛雪音乐音源项目终极指南:如何免费获取全网高品质音乐资源
洛雪音乐音源项目终极指南:如何免费获取全网高品质音乐资源 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 你是否曾为寻找免费的高品质音乐资源而烦恼?或者厌倦了在多个音…...
告别手动复制!用这个BAT脚本一键导出文件夹所有文件名到Excel
告别手动复制!用这个BAT脚本一键导出文件夹所有文件名到Excel 整理文件清单是许多职场人士的日常痛点。想象一下:你刚接手一个包含数百个设计稿的文件夹,领导要求半小时内提交完整的文件清单;或者你需要将一个项目的所有代码文件整…...
前端开发者的Rust入门实战:手把手教你用Tauri为现有Vite项目添加桌面端能力
前端开发者的Rust入门实战:手把手教你用Tauri为现有Vite项目添加桌面端能力 当你的Vite项目需要突破浏览器沙箱限制时,Tauri提供了最优雅的解决方案。作为Electron的现代替代品,它允许前端开发者用熟悉的Web技术栈开发桌面应用,同…...
先抛个干货:这个改进版的黑猩猩优化算法SLWChoA,新手照着敲就能跑,而且效果比原版和不少老算法都强
混合改进策略的黑猩猩优化算法SLWChoA:采用Sobel序列初始化种群,增强种群的多样性和随机性;引入凸透镜成像的反向学习策略,提高算法的收敛速度精度和速度;将水波动态自适应因子添加到攻击者位置更新出,增强…...
Mustache错误处理与调试:7个常见问题排查清单
Mustache错误处理与调试:7个常见问题排查清单 【免费下载链接】mustache Logic-less Ruby templates. 项目地址: https://gitcode.com/gh_mirrors/mu/mustache Mustache是一款流行的无逻辑Ruby模板引擎,但开发者在实际使用中经常会遇到各种错误和…...
在 SAP 系统中,利润中心(Profit Center)和业务范围(Business Area)都是用于内部管理报告的组织单元,但它们在设计理念、功能和应用上存在显著区别。简单来说,利润中心是更现代
在 SAP 系统中,利润中心(Profit Center)和业务范围(Business Area)都是用于内部管理报告的组织单元,但它们在设计理念、功能和应用上存在显著区别。简单来说,利润中心是更现代、更灵活、功能更强…...
如何用QtScrcpy实现跨平台Android设备高效投屏与控制
如何用QtScrcpy实现跨平台Android设备高效投屏与控制 【免费下载链接】QtScrcpy Android实时投屏软件,此应用程序提供USB(或通过TCP/IP)连接的Android设备的显示和控制。它不需要任何root访问权限 项目地址: https://gitcode.com/barry-ran/QtScrcpy 在数字化…...
MAX32630FTHR平台RF95 LoRa精简移植实战
1. RadioHead库深度解析:面向MAX32630FTHR平台的RF95 LoRa通信精简移植 1.1 项目定位与工程价值 RadioHead并非官方标准协议栈,而是由Airspayce公司开发的一套轻量级、跨平台无线通信抽象库。其设计哲学强调“最小可行通信”——不追求协议完备性&#…...
春季2021亚马逊研究奖获奖者公布
春季 2021 某机构研究奖获奖者公布 2021年7月,某机构通知申请人已成为2021年春季某机构研究奖的获得者。该奖项旨在为跨多个学科领域开展研究课题的学术研究人员提供无限制资金和某云平台服务积分。今天,我们正式公布26位获奖者,他们来自11个…...
