2023-6-12-第三式单例模式



🍿*★,°*:.☆( ̄▽ ̄)/$:*.°★* 🍿

💥💥💥欢迎来到🤞汤姆🤞的csdn博文💥💥💥
💟💟喜欢的朋友可以关注一下,下次更新不迷路💟💟
😆😆😆私聊获取个人订阅号哦,欢迎订阅共同学习😆😆😆
💖💖💖💖可以加入大家庭群聊,一起学习天天有福利💖💖💖💖





🍬本文摘要

在这里插入图片描述

设计方法二十三式之单例模式


目录

  • 🍬本文摘要
  • 😉一、基础概念
  • 🐱‍🐉二、单例模式实现
  • 🎉三、单例模式与线程安全
  • 🐱‍🚀四、懒汉模式怎么保证线程安全
  • 🎂五、饿汉模式怎么保证线程安全
  • 🥩六、注意事项
  • 🍳参考文献
  • 🧊文章总结



😉一、基础概念

单例模式(Singleton Pattern)是一种创建型设计模式,它保证某一个类只有一个实例存在,并提供一个全局访问点。

在单例模式中,通过将类的构造函数设为私有,从而防止外部直接创建对象。同时,通过一个静态方法或者变量来获取该类的唯一实例。如果该类的实例不存在,则创建一个新的实例并返回;如果该类的实例已经存在,则直接返回该实例。

单例模式的优点是可以避免因为创建多个实例而导致的资源浪费和性能下降。同时,由于该类只有一个实例存在,因此可以更好地控制该实例的状态和行为。此外,单例模式还可以提供一个全局访问点,使得其他对象可以方便地访问该实例。

需要注意的是,单例模式虽然可以解决某些问题,但也可能会引入新的问题。例如,单例模式可能会导致代码的耦合性增加,使得代码难以测试和维护。因此,在使用单例模式时需要权衡利弊,合理使用。


🐱‍🐉二、单例模式实现

懒汉模式

class Singleton {
public:
    static Singleton* getInstance() {
        static Singleton instance;
        return &instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,Singleton 类的构造函数是私有的,因此外部无法直接创建该类的实例。getInstance() 方法是该类的静态方法,用于获取该类的唯一实例。在 getInstance() 方法中,使用了静态局部变量的方式来创建该类的唯一实例。由于静态局部变量只会在第一次调用时初始化,因此可以保证该类只有一个实例存在。

需要注意的是,在多线程环境下,需要考虑线程安全问题。可以使用加锁的方式来解决该问题。此外,在使用单例模式时,还需要避免使用全局变量或静态变量等方式来创建对象,这样会破坏单例模式的原则。

饿汉模式

class Singleton {
public:
    static Singleton* getInstance() {
        return &instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton instance;
};

Singleton Singleton::instance;

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,Singleton 类的唯一实例是在类定义中直接创建的,因此可以保证在程序启动时就已经创建了该实例,即所谓的“饿汉模式”。需要注意的是,由于该实例是在程序启动时就创建的,因此可能会影响程序启动的速度和内存占用。

在使用饿汉单例模式时,需要注意线程安全问题。如果该类的实例需要在多线程环境下使用,需要使用加锁的方式来保证线程安全。


🎉三、单例模式与线程安全

单例模式的懒汉模式和饿汉模式都可能会造成线程安全问题,具体原因如下:

  1. 懒汉模式

懒汉模式是指在第一次使用时才创建单例对象。在多线程环境下,如果多个线程同时调用 getInstance() 方法,那么可能会创建多个实例,违反了单例模式的原则。例如,当一个线程在判断实例是否存在时,还没有创建实例,此时另一个线程也进入了判断实例是否存在的代码块,也未创建实例,然后两个线程都创建了实例,导致单例模式失效。

  1. 饿汉模式

饿汉模式是指在程序启动时就创建单例对象。在多线程环境下,如果多个线程同时调用 getInstance() 方法,那么可能会访问到未完全初始化的实例,从而导致程序异常或者崩溃。例如,当一个线程正在初始化实例时,另一个线程也调用了 getInstance() 方法,此时可能会访问到未完全初始化的实例。

因此,在实际使用中,需要考虑单例模式的线程安全问题。可以使用加锁的方式来保证线程安全,或者采用其他线程安全的方式来实现单例模式。


🐱‍🚀四、懒汉模式怎么保证线程安全

如果要求在懒汉模式下保证线程安全,并且实例化之后不能再加锁,可以使用双重检查锁定(Double-Checked Locking)的方式来实现。双重检查锁定是一种常用的单例模式实现方式,它可以在保证线程安全的同时,避免了每次调用 getInstance() 方法都需要加锁的性能问题。

具体来说,双重检查锁定的实现方式如下:

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);

            if (instance == nullptr) {
                instance = new Singleton();
            }
        }

        return instance;
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton* instance;
    static std::mutex mutex;
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,getInstance() 方法首先判断实例是否已经存在,如果不存在,则获取互斥锁,然后再次判断实例是否已经存在。由于在获取互斥锁之前已经进行了一次判断,因此可以避免多个线程同时获取互斥锁的情况,从而提高了程序的性能。

需要注意的是,在使用双重检查锁定时,需要注意内存可见性问题。由于编译器和处理器的优化,可能会导致变量的值在不同的线程中不一致,从而导致程序出现异常。可以使用 std::atomic 类型来解决这个问题,或者使用 C++11 中引入的跨平台内存屏障(Memory Barrier)来保证内存可见性。


🎂五、饿汉模式怎么保证线程安全

饿汉模式是指在程序启动时就创建单例对象。在这种情况下,由于实例已经被创建,因此不存在多个线程同时创建实例的问题。所以,饿汉模式本身就是线程安全的。

但是,如果在程序启动时需要进行大量的初始化工作,可能会影响程序的启动速度。因此,可以考虑将初始化工作延迟到实例被第一次使用时再进行,这样可以避免不必要的初始化工作,提高程序的启动速度。

如果需要在饿汉模式下延迟初始化工作,可以使用静态成员变量和静态成员函数的方式来实现。静态成员变量只会在第一次使用时被初始化,因此可以实现延迟初始化的效果。示例代码如下:

class Singleton {
public:
    static Singleton* getInstance() {
        return &getInstanceImpl();
    }

    void doSomething() {
        std::cout << "Singleton doSomething" << std::endl;
    }

private:
    Singleton() {}

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Singleton& getInstanceImpl() {
        static Singleton instance;
        return instance;
    }
};

int main() {
    Singleton* instance1 = Singleton::getInstance();
    Singleton* instance2 = Singleton::getInstance();

    if (instance1 == instance2) {
        std::cout << "instance1 and instance2 are the same instance" << std::endl;
    } else {
        std::cout << "instance1 and instance2 are different instances" << std::endl;
    }

    instance1->doSomething();
    instance2->doSomething();

    return 0;
}

在这个示例中,getInstanceImpl() 方法返回的是一个静态局部变量 instance 的引用。由于静态局部变量只会在第一次使用时被初始化,因此可以实现延迟初始化的效果。在 getInstance() 方法中,直接返回 getInstanceImpl() 方法的返回值即可。

需要注意的是,在使用静态局部变量时,需要考虑线程安全问题。在多线程环境下,可能会出现多个线程同时访问静态局部变量的情况,从而导致程序出现异常。可以使用 C++11 中引入的线程安全的局部静态变量(Thread-safe Local Static)来解决这个问题。


🥩六、注意事项

单例模式是一种常用的设计模式,但是在使用时需要注意以下几点:

  1. 线程安全性:单例模式在多线程环境下需要保证线程安全性,否则可能会导致多个线程同时创建实例的问题。可以使用懒汉模式、饿汉模式、双重检查锁定等方式来保证线程安全性。

  2. 内存泄漏:单例模式在程序运行期间只会创建一个实例,如果实例没有被正确释放,可能会导致内存泄漏问题。可以使用智能指针等方式来避免内存泄漏问题。

  3. 可维护性:单例模式可能会导致代码的可维护性下降,因为单例模式隐藏了对象的创建和销毁过程,使得代码的调试和修改变得困难。因此,在使用单例模式时需要注意代码的可维护性。

  4. 应用场景:单例模式适用于需要全局唯一实例的场景,例如配置文件、日志系统等。但是,在一些场景下,单例模式可能会导致代码的复杂性增加,因此需要根据实际情况来选择是否使用单例模式。

  5. 可测试性:单例模式可能会导致代码的可测试性下降,因为单例模式隐藏了对象的创建和销毁过程,使得代码的测试变得困难。因此,在使用单例模式时需要注意代码的可测试性。


🍳参考文献

🧊文章总结

提示:这里对文章进行总结:

  本文讲了单例模式的一些注意事项,希望大家学习后有所收获。






更多好文推荐

🍸2021-4月Python 机器学习——中文新闻文本标题分类
🍹2021年4月-(计算机网络)小型校园网络模拟搭建,最全最准确版
🍺2022-10-31-基于用户的协同过滤推荐算法实现+MAE+RMSE
🍻2022-11-28-大数据可视化,特征维度大于50
🥂2023-3-9-一篇简短的文章把C++左右值关系讲的透透彻彻

上一篇
End
下一篇

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

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

相关文章

HTTPS

HTTP 协议内容都是按照文本的方式明文传输的。 这就导致在传输过程中出现一些被篡改的情况。为了保证安全&#xff0c;现在大多数网站都采用HTTPS协议。HTTPS协议是在HTTP协议的基础上引入了一个加密层SSL。 目录 HTTPS的加密流程对称加密非对称加密为什么引入非对称加密&…

Python处理办公自动化的10大场景

在编程世界里&#xff0c;Python已经是名副其实的网红了。Python最大优势在于容易学&#xff0c;门槛比Java、C低非常多&#xff0c;给非程序员群体提供了用代码干活的可能性。当然Python能成为大众编程工具&#xff0c;不紧是因为易学&#xff0c;还因为Python有成千上万的工具…

抖音电商发展路径:从外链种草到达人/品牌直播

复盘抖音电商发展&#xff0c;可以总结出以下几点发展特征&#xff1a; 策略重心的变化 以种草为核心&#xff0c;给电商引流站外成交&#xff08;2019 年及之前&#xff09;→ 力推达人直播但效 果一般&#xff08;2020 上半年&#xff09;→ 推品牌自播并彻底闭环&#xff0…

Redis.conf 详解

我们启动 Redis&#xff0c;一般都是通过 Redis.conf 启动。 因此&#xff0c;我们必须了解 Redis.conf 的配置&#xff0c;才能更好理解和使用 Redis。 单位 单位注意事项&#xff1a;当需要内存大小时&#xff0c;可以指定为1k 5GB 4M等 通常形式&#xff1a; 1k > 1000字…

谈谈几个常见数据结构的原理

数组 数组是最常用的数据结构&#xff0c;创建数组必须要内存中一块 连续 的空间&#xff0c;并且数组中必须存放 相同 的数据类型。比如我们创建一个长度为10&#xff0c;数据类型为整型的数组&#xff0c;在内存中的地址是从1000开始&#xff0c;那么它在内存中的存储格式如…

【lvs集群】HAProxy搭建Web集群

HAProxy搭建Web集群 一、 HAProxy简介1.1HAProxy主要特性1.2HAProxy负载均衡策略非常多&#xff0c;常见的有如下8种1.3LVS、Nginx、HAproxy的区别1.4常见的Web集群调度器 二、Haproxy搭建 Web 群集haproxy服务器部署节点服务器部署 三、定义监控页面与定义日志3.1定义监控页面…

Multimodal fusion via cortical network inspired losses(第一次优质论文分享)

Multimodal fusion via cortical network inspired losses 论文介绍1. 论文研究的任务是什么&#xff1f;2. 论文关注/拟解决的问题是什么&#xff1f;3. 论文提出什么方法如何解决这个问题&#xff1f;4. 如何设计实验 来证明 所提方法确实解决了 拟解决的问题&#xff1f; 论…

kotlin协程flow retry功能函数返回失败后重试(4)

kotlin协程flow retry功能函数返回失败后重试&#xff08;4&#xff09; import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlockingfun main(args: Array<String>) {var count 0 //重试计数runBlocking {load().onEach…

RetinaNet网络介绍

前言 上一篇博文我们介绍了Focal Loss&#xff0c;原理也比较简单&#xff0c;有不了解的小伙伴可以先跳转到之前的博文了解一下。Focal Loss介绍。这篇博文我们来看下Focal Loss的出处&#xff1a;Focal Loss for Dense Object Detection&#xff0c;这篇论文提出了RetainNet之…

chatgpt赋能python:Python怎么建服务器?

Python怎么建服务器&#xff1f; 作为一名具有10年Python编程经验的工程师&#xff0c;我深入研究了Python的一些高级特性&#xff0c;其中包括Python如何建立服务器的方法。Python是一个高级的编程语言&#xff0c;可以轻松创建服务器应用程序&#xff0c;并为您的网站提供高…

低秩矩阵(Low-Rank)的意义

&#xff11;&#xff0e;回顾基础&#xff1a; 矩阵的秩度量的是矩阵行列之间的相关性&#xff0c;如果各行各列都是线性无关的&#xff0c;矩阵就是满秩。非零元素的行或列决定了秩的大小。&#xff0f;&#xff0f;划重点&#xff0c;秩可以度量矩阵自身相关性 讲个小故事…

windows 服务程序和桌面程序集成(七)效果演示及源程序下载

系列文章目录链接 windows 服务程序和桌面程序集成&#xff08;一&#xff09;概念介绍windows 服务程序和桌面程序集成&#xff08;二&#xff09;服务程序windows 服务程序和桌面程序集成&#xff08;三&#xff09;UDP监控工具windows 服务程序和桌面程序集成&#xff08;四…

计算机提示“找不到vcruntime140.dll,无法继续执行代码可”以这样子修复

首先&#xff0c;对于那些不熟悉的人来说&#xff0c;vcruntime140.dll是一个关键文件&#xff0c;用于在Windows操作系统上运行使用C语言编写的大型应用程序。如果你正在运行或安装这样的应用程序&#xff0c;但找不到vcruntime140.dll文件&#xff0c;那么你的应用程序可能无…

Maven私服

Maven 私服是一种特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;用来代理位于外部的远程仓库&#xff08;中央仓库、其他远程公共仓库&#xff09;。 建立了 Maven 私服后&#xff0c;当局域网内的用户需要某个构件时&#xff0c;会按照如下顺序进行请求…

低代码崛起:会让程序员饭碗不保,人工智能或成其催化剂

人工智能技术目前发展的趋势如何 关于人工智能技术的评价&#xff0c;大众的评价几乎算是较为一致的&#xff0c;都认为其已成为人类有史以来最具革命性的技术之一。当然了&#xff0c;可能目前的我们还是很难想象机器自主决策所产生的影响&#xff0c;但可以肯定的是&#xff…

ELF文件结构和实战分析

文章目录 示例编译运行 ELF文件格式ELF HeaderELF Section Header Table (节头表)sh_typesh_flagssh_link、sh_info 节链接信息 ELF Sections节的分类.text节.rodata节.plt节&#xff08;过程链接表&#xff09;.data节.bss节.got.plt节&#xff08;全局偏移表-过程链接表&…

ArkTS语言HarmonyOS/OpenHarmony应用开发-message事件刷新卡片内容

开发过程 在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility&#xff0c;然后由FormExtensionAbility刷新卡片内容。 common&#xff1a;公共文件 通过点击button按钮&#xff0c;刷新卡片内容。代码示例&#xff1a; WidgetCard.ets let stor…

内网渗透—Linux上线

内网渗透—Linux上线 1. 前言2. 下载插件3. CS配置3.1. 客户端配置3.1.1. 导入插件文件3.1.2. 配置监听 3.2. 服务端配置3.2.1. 导入配置文件 3.3. 生成木马3.3.1. 修改cna文件3.3.2. 修改后效果 3.4. 执行木马 1. 前言 默认情况下CS是不支持上线Linux的&#xff0c;只支持上线…

learn C++ NO.6——类和对象(4)

1.再谈构造函数 1.1.构造函数体赋值 在创建类的对象时&#xff0c;编译器回去调用类的构造函数&#xff0c;来各个成员变量一个合适的值。 class Date { public:Date(int year,int month,int day){_year year;_month month;_day day;}private:int _year;int _month;int _…

软件测试必备7大技能

一、测试用例的编写 1.在测试中最重要的文档&#xff0c;他是测试工作的核心&#xff0c;是一组在测试时输入输出的标准&#xff0c;是软件需求的具体对照。编写测试用例&#xff0c;是测试人员的基本功&#xff0c;真正能写好的人并不多。 2.测试用例包含的内容&#xff1a;…