【GeekBand】C++设计模式笔记12_Singleton_单件模式

news2024/11/18 13:02:41

1. “对象性能” 模式

  • 面向对象很好地解决了 “抽象” 的问题, 但是必不可免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
  • 典型模式
    • Singleton
    • Flyweight

2. Singleton 单件模式

2.1 动机(Motivation)

  • 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性以及良好的效率。
  • 如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
  • 这应该是类设计者的责任,而不是使用者的责任。

2.2 模式定义

保证一个类仅有一个实例,并提供一个该实例的全局访问点
——《设计模式》GoF

2.3 实例代码

class Singleton {
private:
	// 将构造函数、拷贝构造函数设置成 private,不允许用户创建实例
    Singleton();
    Singleton(const Singleton& other);
    
public:
	// 提供实例的全局访问点
    static Singleton* getInstance();

private:
	// static 成员,唯一的实例
    static Singleton* m_instance;
};

// static 成员初始化
Singleton* Singleton::m_instance = nullptr;

/*
线程非安全版本,多线程环境下有可能创建出多个实例,
当线程A在执行完(1)、执行(2)之前,线程B抢到时间片,开始执行(1),这样线程A和B都可以创建出实例,
所以在多线程环境中需要加锁。
*/ 
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {		// (1)
        m_instance = new Singleton();	// (2)
    }
    return m_instance;
}

/*
线程安全版本,但锁的代价过高,
因为锁的粒度粗,进入函数后立马加锁,整个函数过程都持有锁,
一般m_instance是只读的,当m_instance创建完成后,各线程可以直接获取该实例,
该代码既有问题:当线程A持有锁时,线程B无法获取m_instance,必须等待线程A释放锁。
*/
Singleton* Singleton::getInstance() {
    Lock lock;
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

/*
双检查锁,但由于内存读写reorder不安全
reorder含义:编译器可能改变对象构造的顺序
常规对象构造分为3个步骤:a.分配空间;b.调用构造函数完成对象初始化;c.返回对象的指针;但编译器有时会改变这3个顺序,变成a.c.b。
这样代码就会出现问题:当m_instance未初始化时,线程A需要对m_instance进行实例化,若当线程A完成步骤a和c,而未完成b时,线程B调用该函数,由于此刻 m_instance != nullptr,会直接返回m_instance,但该实例尚未完成初始化,直接使用该实例会产生未定义行为。
(2)处的二次检查必不可少:当AB两个线程都通过(1)后开始抢锁,假设A抢到了锁,A对m_instance进行实例化,当线程A释放锁线程B获得锁后,如果没有(2)处的检查,线程B将再次对m_instance初始化,不符合单例模式设计原则。
*/
Singleton* Singleton::getInstance() { 
    if (m_instance == nullptr) {	// (1)
        Lock lock;
        if (m_instance == nullptr) {		// (2),这里的二次检查必不可少
            m_instance = new Singleton();	// (3)
        }
    }
    return m_instance;
}

// C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);	// 获取内存fence
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);	// 释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

2.4 结构(Structure)

在这里插入图片描述

2.5 要点总结

  • Singleton 模式中的实例构造器可以设置为 protected 以允许子类派生。
  • Singleton 模式一般不要支持拷贝构造函数和 Clone 接口,因为这有可能导致多个对象实例,与 Singleton 模式的初衷违背。
  • 如何实现多线程环境下安全的 Singleton ?注意对双检查锁的正确实现。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

架构篇(理解架构的模式2)

目录 一、管理和监控 大使模式&#xff1a;创建代表消费者服务或应用程序发送网络请求的帮助服务 反腐模式&#xff1a;在现代应用程序和遗留系统之间实现装饰或适配器层 外部配置存储&#xff1a;将应用程序部署包中的配置信息移动到中心化的位置 网关聚合模式&#xff1…

20241116解决在WIN11和ubuntu20.04通过samba共享时出现局域网千兆带宽拉满的情况

20241116解决在WIN11和ubuntu20.04通过samba共享时出现局域网千兆带宽拉满的情况 2024/11/16 13:42 缘起&#xff1a;最近需要通过iperf3打流&#xff0c;因此在ubuntu20.04服务器上常开sudo nethogs监控流量。 但是发现一个异常&#xff0c;ubuntu20.04服务器上发送的流量过大…

DevOps工程技术价值流:打造卓越项目协作的优化宝典

一、引言 解锁项目协作的无限潜力&#xff0c;覆盖全链路实现流畅高效。 在当今瞬息万变的商业环境中&#xff0c;项目协作的效率和效果直接关系到企业的竞争力和市场响应速度。DevOps工程技术价值流中的项目协作优化&#xff0c;不仅是技术层面的革新&#xff0c;更是团队协…

WSL--无需安装虚拟机和docker可以直接在Windows操作系统上使用Linux操作系统

安装WSL命令 管理员打开PowerShell或Windows命令提示符&#xff0c;输入wsl --install&#xff0c;然后回车 注意&#xff1a;此命令将启用运行 WSL 和安装 Linux 的 Ubuntu 发行版所需的功能。 注意&#xff1a;默认安装最新的Ubuntu发行版。 注意&#xff1a;默认安装路径是…

⾃动化运维利器Ansible-基础

Ansible基础 一、工作原理二、快速入门2.1 测试所有资产的网络连通性2.2 发布文件到被管理节点(资产) 三、资产(被管理节点)3.1 静态资产3.1.1 自定义资产3.1.2 自定义资产的使用3.1.3 资产选择器 四、Ansible Ad-Hoc 命令4.1 模块类型4.1.1 command & shell 模块4.1.2 cop…

简易实现自动签到并发送通知邮件

环境准备 Windows操作系统adbshell1.0.40pyhon3.7visual stdio code stableandroid手机数据线&#xff0c;并配置环境变量 打卡程序 需要定位屏幕坐标 import os import timea0os.popen("adb shell input keyevent 26") ##ba0.read() ##print(b) time.sleep(5) …

机器学习(贝叶斯算法,决策树)

朴素贝叶斯分类 贝叶斯分类理论 假设现有两个数据集&#xff0c;分为两类 我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中红色圆点表示的类别)的概率&#xff0c;用p2(x,y)表示数据点(x,y)属于类别2(图中蓝色三角形表示的类别)的概率&#xff0c;那么对于一个新数据点(x,y)…

[ACTF2020]Upload 1--详细解析

信息收集 题目告诉我们是一道upload&#xff0c;也就是文件上传漏洞题目。 进入界面&#xff0c;是一个灯泡&#xff0c;将鼠标放在图标上就会出现文件上传的相应位置&#xff1a; 思路 文件上传漏洞&#xff0c;先看看有没有前端校验。 在js源码中找到了前端校验&#xff…

网络常用特殊地址-127.0.0.1

借用Medium博客的一张图 经常在问题解答群里留意到如下关于127.0.0.1的消息 ”如果单机版&#xff0c;不需要配置IP&#xff0c;所有配置IP的地方都写死127.0.0.1就可以” “ip: 根据实际情况填写&#xff08;在 xxx-init.conf 里可以给一个默认值 127.0.0.1 &#xff0c;方便…

Scala-字符串(拼接、printf格式化输出等)-用法详解

Scala 一、 使用 号连接字符串 在 Scala 中&#xff0c; 运算符实际上会调用 String 类的 concat 方法或者使用字符串的加法操作&#xff0c;生成一个新的字符串。 字符串是不可变的&#xff0c;每次拼接都会创建一个新的字符串。 Mr. yuTips&#xff1a; 性能相对较差&…

软考教材重点内容 信息安全工程师 第 4 章 网络安全体系与网络安全模型

4,1 网络安全体系的主要特征: (1)整体性。网络安全体系从全局、长远的角度实现安全保障&#xff0c;网络安全单元按照一定的规则&#xff0c;相互依赖、相互约束、相互作用而形成人机物一体化的网络安全保护方式。 (2)协同性。网络安全体系依赖于多种安全机制&#xff0c;通过各…

【数据库】如何保证数据库迁移过程中数据的一致性?

在数据库迁移过程中&#xff0c;保证数据的一致性是非常重要的&#xff0c;尤其是在涉及到多个表、多个数据库或分布式系统的情况下。以下是一些确保数据一致性的最佳实践和方法&#xff1a; 1. 备份数据 在开始迁移之前&#xff0c;进行全面的数据备份是确保数据一致性的第…

github 模型下载方法

github 模型权重&#xff0c;如果是项目下载&#xff0c;pth文件有时下载后只有1kb 本人测试ok下载方法&#xff1a; 点击view raw&#xff0c;然后可以下载模型权重文件了。

spring-data-elasticsearch 3.2.4 实现桶bucket排序去重,实现指定字段的聚合搜索

一、背景 es索引有一个文档CourseIndex&#xff0c;下面是示意: creatorIdgradesubjectnameno1002270英语听力课程一N00232DS91004380数学口算课程N00209DK71003480物理竞赛课程N00642XS21002280英语听力课程二N00432WS31002290英语听力课程三N002312DP5 在搜索的时候&#…

QQ 小程序已发布,但无法被搜索的解决方案

前言 我的 QQ 小程序在 2024 年 8 月就已经审核通过&#xff0c;上架后却一直无法被搜索到。打开后&#xff0c;再在 QQ 上下拉查看 “最近使用”&#xff0c;发现他出现一下又马上消失。 上线是按正常流程走的&#xff0c;开发、备案、审核&#xff0c;没有任何违规&#xf…

快速搭建Android开发环境:Docker部署docker-android并实现远程连接

目录 前言 1. 虚拟化环境检查 2. Android 模拟器部署 3. Ubuntu安装Cpolar 4. 配置公网地址 5. 远程访问 小结 6. 固定Cpolar公网地址 7. 固定地址访问 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊快速搭建Android开发环境&#x…

大麦抢票科技

仅供学习参考&#xff0c;切勿再令您所爱的人耗费高昂的价格去购置黄牛票 ⚠️核心内容参考: 据悉&#xff0c;于购票环节&#xff0c;大麦凭借恶意流量清洗技术&#xff0c;于网络层实时甄别并阻拦凭借自动化手段发起下单请求的流量&#xff0c;强化对刷票脚本、刷票软件以及…

【STM32】基于SPI协议读写SD,详解!

文章目录 0 前言1 SD卡的种类和简介1.1 SD卡的种类1.2 SD卡的整体结构1.3 SD卡运行机制——指令和响应2 SD卡的通信总线2.1 SDIO2.2 SPI3 硬件连接4 代码实践【重点】4.1 HAL库移植4.2 标准库移植4.3 遇到的问题和解决方案5 扩展阅读0 前言 因为项目需要,使用stm32读写sd卡,这…

kafka管理工具

文章目录 前言一、Kafka Assistan1.1 描述1.2、配置安装 二、Conduktor2.1、描述2.2、配置安装 三、kafka-maneger3.1、描述3.2、配置安装3.3、命令启动3.4、[refer to](https://www.ctyun.cn/document/10000120/10033218#section-39755766f4910e4b) 前言 提示&#xff1a;这里…

leetcode_二叉树最大深度

对二叉树的理解 对递归调用的理解 对内存分配的理解 基础数据结构&#xff08;C版本&#xff09; - 飞书云文档 每次函数的调用 都会进行一次新的栈内存分配 所以lmax和rmax的值不会混在一起 /*** Definition for a binary tree node.* struct TreeNode {* int val;* …