【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]

news2024/11/15 20:38:28

在这里插入图片描述

阅读导航

  • 引言
  • 一、设计模式概念(了解)
  • 二、单例模式
    • 1. 饿汉模式
      • (1)概念
      • (2)模拟实现
      • (3)优缺点
      • (4)适用场景
    • 2. 懒汉模式
      • (1)概念
      • (2)模拟实现
        • 🚩思路一(双检查加锁,常规思路)
        • 🚩思路二(使用静态局部变量的方式来实现单例模式)
      • (3)优缺点
      • (4)适用场景
  • 温馨提示

引言

在面向对象编程中,特殊类是指具有特定属性或限制的类,这些属性或限制使其在设计和使用上与常规类不同。在上一篇文章中,我们讨论了一些特殊类,如只能在堆上创建对象的类、只能在栈上创建对象的类以及禁止拷贝和继承的类。

在本文中,我们将继续探讨特殊类的设计,着重介绍单例模式。单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供了全局访问点。在许多情况下,我们需要确保只有一个对象来协调系统操作或管理共享资源,而单例模式正是解决这类问题的理想选择

本文将深入研究单例模式的原理和实现方式。我们将介绍几种常见的单例模式实现方法,包括饿汉式、懒汉式、双重检查锁定和静态内部类。我们将详细讨论每种实现方法的优缺点,并提供相应的示例代码。让我们一起探索单例模式的精髓吧!

一、设计模式概念(了解)

设计模式是一种被广泛接受和应用的软件开发经验总结,它提供了解决常见问题的可重用方案。设计模式帮助开发人员以一种可靠、灵活和可维护的方式构建软件系统。

设计模式的概念最早由计算机科学家埃里希·伽玛Erich Gamma)等人在1994年的著作《设计模式:可复用面向对象软件的基础》中引入。该书提出了23种经典的设计模式,这些模式分为三大类:创建型模式、结构型模式和行为型模式。

每种设计模式都有其特定的应用场景和解决方案,开发人员可以根据具体需求选择适当的模式来解决问题。设计模式不仅提供了一种通用的解决方案,还促进了代码的可读性、可维护性和可扩展性。

然而,设计模式并非万能药,过度使用或错误使用设计模式可能导致代码变得复杂和难以理解。因此,在应用设计模式时,开发人员需要谨慎权衡,并结合实际情况做出决策。

总之,设计模式是一种帮助开发人员解决常见问题的工具,它提供了一套经过验证的解决方案。通过学习和应用设计模式,开发人员可以提高软件系统的质量和可维护性,从而更加高效地开发出优秀的软件。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样

二、单例模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

⭕单例模式有两种实现模式:饿汉模式懒汉模式,下面我会一个一个的向大家介绍

1. 饿汉模式

(1)概念

饿汉模式是单例模式的一种实现方式,它在类加载时就创建唯一的实例对象,并通过静态方法提供全局访问点。简单来说就是不管你将来用不用,程序启动时就创建一个唯一的实例对象

🍪特点

  • 在类加载时就创建实例对象,因此可以保证实例的唯一性。
  • 通过静态方法提供全局访问点,方便其他代码获取该实例。
  • 线程安全,由于在类加载时创建实例,因此不需要考虑多线程并发访问的问题。

(2)模拟实现

// 饿汉模式:一开始(main函数之前)就创建对象
class Singleton
{
public:
    // 静态方法,返回唯一实例对象的地址
    static Singleton* GetInstance()
    {
        return _ins;
    }

    // 向字符串向量中添加元素,保证线程安全
    void Add(const string& str)
    {
        _mtx.lock();    // 获取互斥锁

        _v.push_back(str);  // 执行操作

        _mtx.unlock();  // 释放互斥锁
    }

    // 打印字符串向量中的所有元素,保证线程安全
    void Print()
    {
        _mtx.lock();    // 获取互斥锁

        for (auto& e : _v)
        {
            cout << e << endl;
        }
        cout << endl;

        _mtx.unlock();  // 释放互斥锁
    }

private:
    // 构造函数私有化,禁止外部创建对象
    Singleton()
    {}

    // 防拷贝构造和赋值运算符,保证实例的唯一性
    Singleton(const Singleton& s) = delete;
    Singleton& operator=(const Singleton& s) = delete;

private:
    mutex _mtx;         // 互斥锁,保证线程安全
    vector<string> _v;  // 字符串向量,存储数据

    static Singleton* _ins; // 唯一实例对象的地址
};

// 初始化静态成员变量
Singleton* Singleton::_ins = new Singleton();

以上代码实现了一个简单的使用饿汉模式实现的线程安全的单例类。它在类加载时就创建了唯一的实例对象,并提供了全局访问点,适用于需要在整个应用程序中共享一个实例对象的场景。同时,该类的实现还保证了多线程并发访问时的线程安全性,避免了数据竞争和死锁等问题。

(3)优缺点

  • 优点

    1. 实现简单直观,代码易于理解
    2. 线程安全,不需要额外的同步处理,适合在多线程环境中使用。
    3. 对象的创建是在类加载时完成的,可以避免线程安全问题和延迟加载的复杂性
  • 缺点

    1. 在程序运行期间始终存在实例对象,可能会造成资源浪费
    2. 如果该实例对象的创建过程耗时较长,会导致应用程序启动变慢
    3. 不支持延迟加载,无法根据实际需要来创建实例

(4)适用场景

  • 对象的创建过程简单且耗时较短的情况,适合使用饿汉模式。
  • 需要在整个应用程序中共享一个实例对象的情况,适合使用饿汉模式。
  • 在多线程环境下需要保证实例的唯一性和线程安全的情况,适合使用饿汉模式。

总的来说:饿汉模式是一种简单有效的单例模式实现方式,适合于对象创建耗时较短、且需要全局访问的情况。但在实际应用中,需要根据具体需求和性能要求选择适当的单例模式实现方式

2. 懒汉模式

(1)概念

懒汉模式是指在需要时才创建实例对象的单例模式在懒汉模式中,实例对象的创建被延迟到第一次使用时,而不是在程序启动时就立即创建。这样可以避免在程序启动时创建不必要的实例对象,节省系统资源

如果单例对象的构造过程耗时且资源占用较多,例如加载插件、初始化网络连接或读取文件等操作,同时在程序运行过程中可能并不经常使用该对象,那么在程序启动时立即进行初始化会导致启动速度缓慢。因此,在这种情况下,采用懒汉模式(延迟加载)是更好的选择

⭕懒汉模式允许在需要使用该对象时才创建实例,避免了不必要的资源浪费,提高了程序性能。通过懒汉模式,可以延迟加载单例对象,无需在程序启动时进行初始化,从而避免了启动时的缓慢问题。

(2)模拟实现

🚩思路一(双检查加锁,常规思路)
class Singleton
{
public:
    static Singleton* GetInstance()
    {
        // 双检查加锁,提高效率
        if (_ins == nullptr)  // 第一次检查
        {
            _imtx.lock();  // 加锁

            if (_ins == nullptr)  // 第二次检查,确保线程安全
            {
                _ins = new Singleton;  // 创建单例对象
            }

            _imtx.unlock();  // 解锁
        }

        return _ins;
    }

    // 显示释放单例对象
    static void DelInstance()
    {
        _imtx.lock();  // 加锁
        if (_ins)
        {
            delete _ins;  // 释放单例对象
            _ins = nullptr;  // 将指针置为空
        }
        _imtx.unlock();  // 解锁
    }

    // 内部类:用于单例对象的资源回收和持久化
    class GC
    {
    public:
        ~GC()
        {
            DelInstance();  // 调用DelInstance()函数进行资源回收和持久化
        }
    };

    // 内部静态成员变量,用于实现单例模式
    static Singleton* _ins;
    static mutex _imtx;  // 互斥锁,保证线程安全

    // 添加数据到vector中
    void Add(const string& str)
    {
        _vmtx.lock();  // 加锁

        _v.push_back(str);  // 添加数据到vector中

        _vmtx.unlock();  // 解锁
    }

    // 输出vector中的数据
    void Print()
    {
        _vmtx.lock();  // 加锁

        for (auto& e : _v)
        {
            cout << e << endl;  // 输出vector中的数据
        }
        cout << endl;

        _vmtx.unlock();  // 解锁
    }

    // 析构函数,用于实现单例对象的持久化
    ~Singleton()
    {
        // 比如要求程序结束时,将数据写到文件,单例对象析构时持久化就比较好
    }

private:
    // 私有构造函数,限制类外部创建对象
    Singleton()
    {}

    // 防拷贝
    Singleton(const Singleton& s) = delete;
    Singleton& operator=(const Singleton& s) = delete;

    mutex _vmtx;  // 互斥锁,保证线程安全
    vector<string> _v;  // 存储数据的vector

    static GC _gc;  // 内部类对象,用于单例对象析构时进行资源回收和持久化
};

// 初始化静态成员变量
Singleton* Singleton::_ins = nullptr;
mutex Singleton::_imtx;
Singleton::GC Singleton::_gc;

🚩思路二(使用静态局部变量的方式来实现单例模式)
class Singleton
{
public:
    // 获取单例对象的接口函数
    static Singleton* GetInstance()
    {
        // 使用静态局部变量实现单例模式,保证线程安全
        // C++11之前,这里不能保证初始化静态对象的线程安全问题
        // C++11之后,这里可以保证初始化静态对象的线程安全问题
        static Singleton inst;

        return &inst;
    }

    // 添加数据到vector中
    void Add(const string& str)
    {
        // 加锁,保证线程安全
        _vmtx.lock();

        // 添加数据到vector中
        _v.push_back(str);

        // 解锁,保证线程安全
        _vmtx.unlock();
    }

    // 输出vector中的数据
    void Print()
    {
        // 加锁,保证线程安全
        _vmtx.lock();

        // 遍历vector,输出其中的元素
        for (auto& e : _v)
        {
            cout << e << endl;
        }
        cout << endl;

        // 解锁,保证线程安全
        _vmtx.unlock();
    }

    // 析构函数,用于实现单例对象的持久化
    ~Singleton()
    {
        // 比如要求程序结束时,将数据写到文件,单例对象析构时持久化就比较好
    }

private:
    // 私有构造函数,限制类外部创建对象
    Singleton()
    {
        cout << "Singleton()" << endl;
    }

    // 防拷贝
    Singleton(const Singleton& s) = delete;
    Singleton& operator=(const Singleton& s) = delete;

private:
    mutex _vmtx;  // 互斥锁,保证线程安全
    vector<string> _v;  // 存储数据的vector
};

这段代码是使用静态局部变量的方式来实现单例模式。

🚨🚨注意:在 C++11 之前,使用静态局部变量的方式需要注意线程安全问题,因为静态局部变量的初始化只会在第一次调用时进行,如果有多个线程同时调用,可能会导致不同步的问题。但在 C++11 之后,静态局部变量的初始化是线程安全的,因此可以放心使用

(3)优缺点

  • 优点

    1. 延迟加载:懒汉模式在需要时才创建实例对象,避免了在程序启动时的资源浪费。这对于资源消耗较大的对象特别有用。
    2. 节省系统资源:由于实例对象的创建被延迟到需要时,懒汉模式可以节省系统资源,提高程序的性能。
    3. 线程安全:通过双重判断锁机制,懒汉模式可以在多线程环境下保证线程安全性。
  • 缺点

    1. 复杂性增加:相比饿汉模式,懒汉模式的实现相对复杂,需要考虑线程安全性问题。
    2. 性能损耗:在多线程环境下,由于需要进行双重判断锁机制,可能会导致一定的性能损耗。

(4)适用场景

  • 对象创建耗时较长或占用较多资源:懒汉模式可以避免在程序启动时创建不必要的实例对象,节省系统资源。
  • 需要延迟加载的场景:如果单例对象在程序运行的早期并不会被频繁使用,而只有在特定条件下才会被需要,那么懒汉模式是一个合适的选择。
  • 多线程环境下需要保证线程安全性:通过双重判断锁机制,懒汉模式可以在多线程环境下保证线程安全性,避免多个线程同时创建多个实例对象。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

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

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

相关文章

【C++进阶08】哈希的应用(位图and布隆过滤器)

一、位图 1.1 位图的概念 面试题 给40亿个不重复的无符号整数&#xff0c;没排过序 给一个无符号整数&#xff0c;如何快速判断一个数是否在 这40亿个数中。【腾讯】 能想到的解决思路&#xff1a; 遍历&#xff0c;时间复杂度O(N)排序(O(NlogN)) 利用二分查找: logN放到哈…

计算机毕业设计 | SpringBoot+vue 教务管理系统(附源码)

1&#xff0c;项目背景 教育需求增长 随着社会的发展&#xff0c;对于教育质量的要求也在不断提高。传统的手工操作和纸质记录已经不能满足现代教学的需求。因此&#xff0c;一个自动化、数字化的教务管理系统成为了必然的选择。 信息化趋势 现代科技的飞速发展使得信息化成为…

【算法】登山(线性DP,最长上升)

题目 五一到了&#xff0c;ACM队组织大家去登山观光&#xff0c;队员们发现山上一共有N个景点&#xff0c;并且决定按照顺序来浏览这些景点&#xff0c;即每次所浏览景点的编号都要大于前一个浏览景点的编号。 同时队员们还有另一个登山习惯&#xff0c;就是不连续浏览海拔相…

如何保证MySQL数据一致性

在当今大数据时代&#xff0c;数据库系统扮演着至关重要的角色&#xff0c;而MySQL作为一种流行的关系型数据库管理系统&#xff0c;在数据一致性方面拥有着丰富的机制和技术。下面简单的探讨MySQL是如何保证数据一致性的。 事务与ACID特性 要了解MySQL如何保证数据一致性&am…

excel中去掉单元格中两个数字之间的空格

excel中去掉单元格中两个数字之间的空格 使用公式&#xff1a;SUBSTITUTE(A1," “,”") 解释&#xff1a;将A1单元格中的空格查找出来并去掉。

香蕉派BPI-M7 瑞芯微RK3588 人工智能开源硬件开发板公开发售

香蕉派(Banana Pi) BPI-M7瑞芯微K3588开源硬件单板计算机公开销售&#xff0c;支持WiFi 6和BT5.2&#xff0c;硬件有3个版本:8G Ram64G eMMC, 16G Ram128 eMMC和32G Ram128 eMMC 香蕉派BPI-M7采用睿芯最新旗舰RK3588八核64位处理器&#xff0c;最高频率为2.4GHz, 6 TOPS NPU&…

网络安全全栈培训笔记(59-服务攻防-中间件安全CVE复现lSApacheTomcataNginx)

第59天 服务攻防-中间件安全&CVE复现&lS&Apache&Tomcata&Nginx 知识点&#xff1a; 中间件及框架列表&#xff1a; lIS,Apache,Nginx,Tomcat,Docker,Weblogic,JBoos,WebSphere,Jenkins, GlassFish,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp,Sprng,Flask,…

MicrosoftEdge浏览器打开网页出现“此网站被人举报不安全”问题时解决办法

1&#xff1a;有时候不知怎么回事用电脑自带的微软浏览器进行搜索会出现以下的问题 这可能是由于我们的浏览器安全审查过于严格引起的 Windows10正式版系统下&#xff0c;使用Edge浏览器浏览网页时候&#xff0c;发现整个页面突然变成了红色&#xff0c;显示“已有人举报此网站…

【项目管理】立项管理

一、前言 对于甲方的立项&#xff1a;需求调研二编写项目申请书一可行性研究&#xff08;机会、初步、详细&#xff09;一项目论证一项目评估一评审获得批准一发布招标文件&#xff01;对于乙方的立项&#xff1a;看到招标文件一进行项目识别一可行性研究&#xff08;机会、初…

MATLAB - 控制小车上的倒立摆

系列文章目录 前言 一、小车 - 摆杆 小车 - 摆杆模型如图 1 所示&#xff0c;使用 Simscape™ Multibody™ 在 Simulink 中建模。 图 1&#xff1a;小车上的倒立摆 图 2&#xff1a;Simscape 多体模型 该系统通过对小车施加可变力 进行控制。控制器需要在将小车移动到新位置或…

【OpenCV】在Linux上使用OpenCvSharp

前言 OpenCV是一个基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习软件库&#xff0c;它具有C&#xff0c;Python&#xff0c;Java和MATLAB接口&#xff0c;并支持Windows&#xff0c;Linux&#xff0c;Android和Mac OS。OpenCvSharp是一个Ope…

Python中类的相关术语(附带案例)

目录 1、面向对象 2、类 3、实例 4、初始化方法 5、魔法方法 6、字符串方法 7、self 8、数据、属性、操作、行为 9、父类、基类、超类 or 子类、派生类 10、多态 11、重载多态 and 重写多态 12、名称解释 1、面向对象 在Python中&#xff0c;面向对象编程&…

由《幻兽帕鲁》私服漏洞引发的攻击面思考

《幻兽帕鲁》私服意外丢档 当了一天的帕鲁&#xff0c;回家开机抓帕鲁的时候发现服务器无法连接。运维工具看了下系统负载发现 CPU 已经跑满。 故障排查 登录服务器进行排查发现存在可疑的 docker 进程。 经过一番艰苦的溯源&#xff0c;终于在命令行历史中发现了端倪 攻击…

深入浅出AI落地应用分析:AI个人助手Monica

前言:铺天盖地的大模型以及所谓的应用到目前为止实际还是很少有像Monica这样贴合个人工作习惯的产品落地,比如像Chatgpt等这样的产品,绝大多数人不会专门买🪜翻墙出去用,而且大多数场景下素人或小白都不知道该怎么用,但是Monica这款产品就很好的以浏览器的插件的形式始终…

[BUUCTF]-PWN:cmcc_pwnme2解析

保护 ida 完整exp&#xff1a; from pwn import* context(log_leveldebug) #premote(node5.buuoj.cn,26964) pprocess(./pwnme2) addhome0x8048644 addflag0x8048682 getfile0x80485CB main0x80486F8 pop_ebp0x8048680 ret0x80483f2 pop_ebx0x8048409 pop_edi_ebp0x804867f st…

贪吃蛇---C语言---详解

引言 C语言已经学了不短的时间的&#xff0c;这期间已经开始C和Python的学习&#xff0c;想给我的C语言收个尾&#xff0c;想起了小时候见过别人的老人机上的贪吃蛇游戏&#xff0c;自己父母的手机又没有这个游戏&#xff0c;当时成为了我的一大遗憾&#xff0c;这两天发现C语…

Unity | 资源热更(YooAsset AB)

目录 一、AssetBundle 1. 插件AssetBundle Browser 打AB包 &#xff08;1&#xff09;Unity&#xff08;我用的版本是2020.3.8&#xff09;导入AssetBundle Browser &#xff08;2&#xff09;设置Prefab &#xff08;3&#xff09;AssetBundleBrowser面板 2. 代码打AB包…

算法:积木游戏学习数学

一、算法描述 小华和小微一起通过玩积木游戏学习数学。 他们有很多积木&#xff0c;每个积木块上都有一个数字&#xff0c;积木块上的数字可能相同。 小华随机拿一些积木挨着排成一排&#xff0c;请小微找到这排积木中数字相同且所处位置最远的2块积木块&#xff0c;计算他们的…

【LeetCode】每日一题 2024_1_30 使循环数组所有元素相等的最少秒数(哈希、贪心、扩散)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;使循环数组所有元素相等的最少秒数题目描述代码与解题思路 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天的题目类型差不多是第一次见到&#xff0c;原来题目描述…

【大数据】Flink 架构(四):状态管理

《Flink 架构》系列&#xff08;已完结&#xff09;&#xff0c;共包含以下 6 篇文章&#xff1a; Flink 架构&#xff08;一&#xff09;&#xff1a;系统架构Flink 架构&#xff08;二&#xff09;&#xff1a;数据传输Flink 架构&#xff08;三&#xff09;&#xff1a;事件…