[C/C++] -- 单例模式

1.简介

单例模式是一种创建型设计模式,它确保类只有一个实例,并提供全局访问点以访问该实例。

引入单例模式目的:

  • 全局访问点: 单例模式提供了一个全局访问点,使得任何地方都可以方便地访问该实例,而不需要通过传递对象的方式。

  • 节省资源: 在某些情况下,创建类的实例可能会消耗较多的资源,例如数据库连接、线程池等。通过使用单例模式,可以避免频繁地创建和销毁对象,从而节省资源和提高性能。

  • 确保唯一性: 单例模式确保类的实例只有一个,避免了因多次创建实例而导致的数据不一致或状态不同步的问题。

  • 控制实例化过程: 单例模式将类的实例化过程集中在一个地方,使得可以更加灵活地控制实例化的时机和方式,例如延迟实例化、懒加载等。

  • 提供全局服务: 单例模式常用于提供全局服务或管理全局状态,例如日志记录器、配置管理器、线程池等。

线程安全

在多线程环境中,确保单例模式的线程安全性至关重要。如果不考虑线程安全性,在多个线程同时调用 getInstance() 方法时,可能会导致多个实例被创建,违反了单例模式的原则。

在C++中,可以通过以下几种方式来确保单例模式的线程安全性:

  • 使用静态初始化: 在C++11及以后的标准中,静态局部变量的初始化是线程安全的。因此,可以将单例实例声明为一个静态局部变量,并在 getInstance() 方法中返回该变量。这样做可以保证在首次调用 getInstance() 方法时,单例实例会被线程安全地初始化。

  • 加锁: 可以使用互斥量(mutex)来实现加锁机制,确保在同一时间只有一个线程可以执行关键代码段。在 getInstance() 方法中,使用互斥量对实例化过程进行加锁,从而确保在多线程环境中只有一个实例被创建。

  • 双重检查锁定(Double-Checked Locking): 双重检查锁定是一种常见的在多线程环境下实现延迟初始化的方法。在 getInstance() 方法中,首先检查实例是否已经被创建,如果尚未创建,则使用互斥量进行加锁,并再次检查实例是否已经被创建。这种方式可以减少在每次调用 getInstance() 方法时都进行加锁的开销。

  • 使用原子操作: C++11引入了一系列的原子操作,如 std::atomic 类和相关的原子操作函数,可以在不需要显式加锁的情况下实现线程安全的操作。可以将单例实例声明为原子类型,从而确保对实例的访问是线程安全的。

2.单例模式实现

单例模式常见以下两类

饿汉式单例模式:还没有获取实例对象,实例对象已经产生

懒汉式单例模式:唯一的实例对象,直到第一次获取它的时候才产生

饿汉式单例模式

在类加载的时候就创建实例,保证了线程安全,但可能会造成资源浪费,因为实例在程序运行期间始终存在,即使没有被使用。

class Singleton
{
public:
    static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法
    {
        return &instance;
    }
private:
    static Singleton instance;      //2.定义一个唯一的类的实例对象
    Singleton()                     //1.构造函数私有化
    {

    }
    //确保单例模式只有一个实例存在
    Singleton(const Singleton &) = delete;//禁用拷贝构造函数
    Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};
Singleton Singleton::instance;      //静态成员变量在类外初始化

int main()
{
    Singleton *p1 = Singleton::getInstance();
    Singleton *p2 = Singleton::getInstance();
    Singleton *p3 = Singleton::getInstance();

    //Singleton t = *p1;无法访问,因为拷贝构造已被禁用
    return 0;
}

懒汉式单例模式

在第一次使用时才进行实例化,避免了资源浪费,但需要考虑线程安全性。常见的线程安全的懒汉式实现方式包括使用加锁的方式或双重检查锁定。

//懒汉式单例:唯一的实例对象,直到第一次获取它的时候才产生
std::mutex mtx;
//懒汉式单例模式-》是不是线程安全的呢?-》线程安全的懒汉式单例模式
class Singleton
{
public:
    //是不是可重入函数?    锁+双重判断
    static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法
    {
        //lock_guard<std::mutex> guard(mtx);//锁的粒度太大了
        if(instance==nullptr)
        {
            lock_guard<std::mutex> guard(mtx);
            if(instance ==nullptr)
            {
                /*
                开辟内存
                构造对象
                给instance赋值
                */
                instance = new Singleton();
            }
        }
        return instance;
    }
private:
    //volatile多线程环境获取到的都是内存的值,变量值可以及时更新
    static Singleton *volatile instance;      //2.定义一个唯一的类的实例对象
    Singleton()                     //1.构造函数私有化
    {

    }
    //确保单例模式只有一个实例存在
    Singleton(const Singleton &) = delete;//禁用拷贝构造函数
    Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};
Singleton*volatile Singleton::instance=nullptr;      //静态成员变量在类外初始化

int main()
{
    Singleton *p1 = Singleton::getInstance();//第一次进来才new对象
    Singleton *p2 = Singleton::getInstance();
    Singleton *p3 = Singleton::getInstance();

    return 0;
}

不使用互斥锁的线程安全的单例模式

C++11及之后的标准中,静态局部变量的初始化会在多线程环境下进行线程安全的操作,避免了竞态条件。因此,尽管没有显式地使用互斥锁来保护临界区,但由于静态局部变量的初始化机制,仍然确保了在多线程环境下只有一个实例被创建。

class Singleton
{
    static Singleton *getInstance() //3.获取类的唯一实例对象的接口方法
    {
        //g++ -o run 单例模式.cpp -g gdb run
        //函数静态局部变量初始化,在汇编指令上已经自动添加线程互斥指令了
        static Singleton instance;//静态成员变量在数据段上,只有第一次调用时才初始化,对象初始化调用构造函数
        return &instance;
    }
private:
    Singleton()                     //1.构造函数私有化
    {
        //很多初始化代码
    }
    //确保单例模式只有一个实例存在
    Singleton(const Singleton &) = delete;//禁用拷贝构造函数
    Singleton &operator=(const Singleton &) = delete;//禁用拷贝赋值运算符
};

int main()
{
    Singleton *p1 = Singleton::getInstance();//第一次进来才new对象
    Singleton *p2 = Singleton::getInstance();
    Singleton *p3 = Singleton::getInstance();

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习记录:AUTOSAR R20-11的阅读记录(五)【CP(5.11-5.19)】完

接上回&#xff1a;学习记录&#xff1a;AUTOSAR R20-11的阅读记录&#xff08;四&#xff09;【CP&#xff08;5.6-5.10&#xff09;】 五、CP 11、General&#xff08;4个&#xff09; 5.11 File Name 说明 1 AUTOSAR_EXP_ LayeredSoftwareArchitecture.pdf 描述了AUTO…

AIGC (AI-Generated Content) 技术深度探索:现状、挑战与未来愿景

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f916; AIGC技术&#xff1a;塑造未来的创意与内容革命 &#x1f31f;引言 &#x1f680;AIGC技术发展现状 &#x1f4c8;核心技术驱动 &#x1f4a1;应用领域拓展 &#x1f310; 面临的挑战 ❌真实性与伦理考量 &am…

Spring @Repository 注解

Spring 的项目严重依赖注解。 Repository 注解 在Spring2.0之前的版本中&#xff0c;Repository注解可以标记在任何的类上&#xff0c;用来表明该类是用来执行与数据库相关的操作(即dao对象)&#xff0c;并支持自动处理数据库操作产生的异常 在Spring2.5版本中&#xff0c;引…

3度带6度带中央经线及带号换算问题

前言&#xff1a;3度带6度带中央经线及代号换算问题是比较繁琐的一个问题&#xff0c;不经常用&#xff0c;公式记不住&#xff0c;因此本文特此整理&#xff0c;以备查。 1.基本概况 我国基本比例尺地形图除1&#xff1a;100万采用兰勃特投影&#xff08;lambert&#xff09;外…

信息系统项目管理师0090:项目经理的影响力范围(6项目管理概论—6.3项目经理的角色—6.3.2项目经理的影响力范围)

点击查看专栏目录 文章目录 6.3.2项目经理的影响力范围1.概述2.项目3.组织4.行业5.专业学科6.跨领域6.3.2项目经理的影响力范围 1.概述 项目经理在其影响力范围内可担任多种角色,这些角色反映了项目经理的能力,体现了项目经理的价值和作用,项目经理会涉及项日、组织、行业、…

爬虫案例:股吧,使用Selenium

爬虫案例:股吧,使用Selenium 实用工具关注公众号爬虫探索者获取。 发送对应关键词: 1.运行环境 pip install selenium==4.9.1 pip install pymysql1.1.高版本Chrome问题 针对于高版本的Chrome浏览器 1.2.最终效果 2.目标网站 3.参数介绍 BASE_URL = https://guba.eas…

ppp和ppp mp理论实验

ppp简介 PPP&#xff08;点对点协议&#xff09;为在点对点连接上传输多协议数据包提供了一个标准方法&#xff0c;是数据链路层封装协议的一种方法&#xff0c;支持同步和异步两种传输方式。&#xff08;除了PPP还有HDLC等&#xff0c;不过HDLC只支持同步方式&#xff09; P…

信号与进程(3):信号及其使用

信号及其使用 参考博客 Linux信号的产生和处理 信号及其使用 信号的产生 信号由内核产生&#xff0c;信号的生成与事件的发生相关&#xff0c;事件的发生源有3类&#xff1a; 1、用户 用户在终端上按下某些按键时会产生信号&#xff0c;如**CtrlC产生SIGINT信号&#xff0…

springboot+mp自动生成没有实体类

mybatisX版本冲突问题 一开始我的MyBatisX版本是1.6.1-3,使用mybatis-plus一直不能正常生成实体类 将MyBatisX的版本换成了1.5.7就可以了 MyBatisX版本更换 1.将原有的MyBatisX卸载后重新安装一个新的版本 2.选择一个合适的版本,这里我选的是1.5.7 下载完成后自己选择一个…

【隧道篇 / WAN优化】(7.4) ❀ 02. WAN优化的作用 ❀ FortiGate 防火墙

【简介】看了上一篇文章&#xff0c;相信大家都知道了在防火墙上启动WAN优化的方法&#xff0c;但是WAN优化到底能做什么&#xff1f;相信有很多人想了解。 什么是WAN优化 现在有许多企业通过集中应用程序或在云中提供应用程序来降低成本并整合资源。应用程序在本地局域网内都能…

python多标签图像分类的图片相册共享交流系统vue+django

建立图片共享系统&#xff0c;进一步提高用户对图片共享信息的查询。帮助用户和管理员提高工作效率&#xff0c;实现信息查询的自动化。使用本系统可以轻松快捷的为用户提供他们想要得到的图片共享信息。 根据本系统的基本设计思路&#xff0c;本系统在设计方面前台采用了pytho…

springboot 引入第三方bean

如何进行第三方bean的定义 参数进行自动装配

CH32V 系列 MCU IAP 使用函数形式通过传参形式灵活指定APP跳转地址

参考: CH32V 系列 MCU IAP 升级跳转方法 CH32V103 的 IAP 问题&#xff08;跳转及中断向量表重定位&#xff09; 1. 沁恒的RISC-V内核MCU的IAP跳转示例程序简要分析 沁恒的RISC-V内核的MCU比如CH32V203、CH32V307等系列的EVT包中IAP升级的示例程序中都是通过使能软件中断之后&…

先进制造业数字化转型,为什么基于传统存储无法完成?

本文是 XSKY 智能存储方案助力先进制造数字化转型系列文章中的第一篇&#xff0c;重点分享先进制造行业数字化转型过程中&#xff0c;对于数据存储的需求&#xff0c;以及为何传统存储架构无法很好满足这些需求。 随着智能制造的发展&#xff0c;自动化、信息化、智能化等技术…

linux命令——fdisk分区

在linux中&#xff0c;一切皆文件&#xff0c;硬盘设备在系统中也以文件形式存在&#xff0c;使用fdisk命令可以查看硬盘分区信息 并非硬盘转速越快&#xff0c;文件读取速率越高&#xff0c;有一个文件存储密度的概念

数据合规官认证证书CCRC-DCO使用设计和默认数据保护处理个人数据

快来了解隐私保护工程实践&#xff01;合法原则是关键&#xff0c;一起守护数据安全&#xff01; 隐私保护工程实践需要遵循合法原则&#xff0c;控制者必须确保处理个人数据有明确的法律依据。在设计和默认数据保护中&#xff0c;相关性、差异化、特定目的、必要性和自主权是合…

在ATECLOUD测试平台测试新能源车内连接器

在测试车内连接器的温度时&#xff0c;需要用到直流电源和温度巡检仪&#xff0c;通过温度巡检仪采集连接器工作时的温度。由于用户在测试时会用到多台直流电源和温度巡检仪&#xff0c;并且型号不一样。因此&#xff0c;在用ATECLOUD测试连接器温度时&#xff0c;技术工程师需…

框架漏洞RCE-1

一、前提 1、命令执行漏洞&#xff1a;直接调用操作系统命令。攻击者构造恶意命令&#xff0c;将命令拼接到正常的输入中&#xff0c;达到恶意攻击的目的。 (1)、常见命令执行函数 PHP&#xff1a;exec、shell_exec、system、passthru、popen、proc_open、反引号等 ASP.N…

拉普拉斯丨独家冠名2024年度ATPV技术分论坛,助力产业科技持续创新

为了进一步促进行业技术交流&#xff0c;推进光伏行业发展及标准建设的进程&#xff0c;针对高效电池&#xff0c;领跑组件&#xff0c;新产品认证及应用等技术专题及国内外光伏标准的最新进程&#xff0c;由中国绿色供应链联盟光伏专委会&#xff08;ECOPV&#xff09;指导的2…
最新文章