C++设计模式——Singleton单例模式

news2024/11/25 10:17:06

一、单例模式的定义  

单例模式,英文全称Singleton Pattern,是一种创建型设计模式,它保证一个类在程序中仅有一个实例,并对外提供一个访问的该类实例的全局接口。

单例模式通常用于需要控制对象资源的开发场景,一个类只创建一个对象的设计,既可以避免创建过多副本所造成的资源浪费现象,又可以避免引发数据一致性等问题。

在数据库连接、线程池设计、日志系统设计等开发场景,经常使用单例模式来创建对象,可以有效地降低对内存资源的占用。

在编码时,为了防止外部直接通过new关键字创建对象实例,Singleton类的构造函数必须是私有的。

在多线程开发场景,单例模式可以避免多个线程同时访问同一个资源,从而避免资源竞争的问题,如果还需要进一步保证线程安全性,可以在创建实例时添加同步锁。

单例模式在现实生活中的抽象实例:

电力公司:在一个城市或地区,通常只有一个电力公司负责供电,我们可以通过该公司来获取电力服务。

总统办公室:在一个国家,通常只有一个总统办公室负责国家事务处理,办公室负责确保国家事务的一致性和高效运作。

登录系统:用户只需要登录一次就可以打开多个应用程序的界面。

二、单例模式的结构

单例模式主要包含以下组件:

1.Singleton类:这是一个单例类,它在整个系统中只有一个实例。通常使用私有构造函数来创建、使用公共静态成员方法来访问。

2.私有构造函数:为了避免被外部实例化,Singleton类通常会定义一个私有构造函数,使得其他类无法通过调用构造函数创建Singleton的实例对象。

3.私有静态成员变量:Singleton类通常会声明一个私有静态成员变量,用来保存Singleton的唯一实例,且这个静态变量只能在Singleton类内部访问。

4.公共的对外接口:它通常被命名为getInstance(),通过该接口可以获取Singleton的唯一实例对象。该接口使用懒汉/饿汉的方式来实现,在接口内部会先判断实例是否已经存在,如果存在则直接返回,否则创建一个新的实例并返回。

组件之间的工作步骤如下:

1.饿汉模式的程序启动:在程序启动的同时就创建好Singleton实例,并提供全局唯一的外部访问接口,外部程序可以直接调用该接口来获取Singleton实例。

2.懒汉模式的程序启动:程序启动时并不会创建Singleton实例,程序在等到单例对象被第一次使用时才创建Singleton实例。

3.采用私有的静态变量存储已经创建好的Singleton实例。

4.外部客户端通过唯一的外部访问接口来访问并使用Singleton实例。

饿汉模式单例 & 懒汉模式单例:

饿汉模式(Singleton with Instantiation):

是一种线程安全的实现方法,在类初始化时就完成了单例实例的创建。

懒汉模式(Singleton with Lazy Initialization):

也被称为"双检锁"模式,只有当第一次真正需要获取单例实例时才进行单例实例的创建。

饿汉模式和懒汉模式都实现了单例,即保证在整个应用程序的生命周期中只有一个Singleton类实例。

饿汉模式的优点是线程安全,但缺点是如果该实例很复杂会增加初始化的耗时,从而导致程序的启动时间被延长。

懒汉模式的优点是延迟加载,可以节约资源和减少程序的启动耗时,缺点是需要考虑多线程环境下创建对象导致的线程安全问题,它通常在外部访问接口中使用双重检查锁定(Double-checked locking)来保证线程安全。

对应UML类图:

三、单例模式代码样例

1.单例模式的伪代码:

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    
    //私有构造函数,确保不能从外部实例化对象
    Singleton() {}
    
public:
    //获取单例实例的静态方法
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singleton = Singleton::getInstance();
    return 0;
}

2.单例模式的加锁版伪代码:

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} //私有化构造函数,防止外部创建实例

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            //在多线程环境下需要加锁保证只创建一个实例
            // std::lock_guard<std::mutex> lock(mutex);
            instance = new Singleton();
        }
        return instance;
    }

    //单例类的其他成员函数
    void doSomething() {
        // ...
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* obj1 = Singleton::getInstance();
    obj1->doSomething();

    Singleton* obj2 = Singleton::getInstance();
    obj2->doSomething();

    delete obj1; //释放资源

    return 0;
}

3.饿汉模式的伪代码:

class Singleton {
private:
    static Singleton* instance;

    //私有构造函数,防止外部直接创建对象
    Singleton() {}

public:
    //静态成员函数,用于获取单例对象
    static Singleton* getInstance() {
        return instance;
    }
};

//提前创建好单例对象
Singleton* Singleton::instance = new Singleton();

4.懒汉模式的伪代码:

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}

public:
    //使用时才创建单例对象
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Demo1:完整代码实现

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    void showMessage() {
        std::cout << "Hello, World!" << std::endl;
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    // 获取Singleton对象实例
    Singleton* singleton = Singleton::getInstance();
    
    // 调用对象的方法
    singleton->showMessage();
    
    return 0;
}

运行结果:

Hello, World!

Demo2:增加析构函数

#include <iostream>

class Singleton {
    public:
    static Singleton& getInstance()
    {
        //如果对象实例不存在就创建一个
        if (!instance) {
            instance = new Singleton();
        }
        return *instance;
    }
    //给外部调用的接口
    void Operation()
    {
        std::cout
            << "Singleton is performing some operation."
            << std::endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    private:
    //私有化的构造函数
    Singleton()
    {
        std::cout << "Singleton instance created."
            << std::endl;
    }
    //私有化的析构函数
    ~Singleton()
    {
        std::cout << "Singleton instance destroyed."
            << std::endl;
    }
    static Singleton* instance;
};

Singleton* Singleton::instance = nullptr;

int main()
{
    Singleton& singleton = Singleton::getInstance();
    singleton.Operation();
    return 0;
}

运行结果:

Singleton instance created.
Singleton is performing some operation.

四、单例模式的优缺点

单例模式的优点:

可以有效限制资源的数量,对于那些需要全局访问的资源,单例模式可以保证该资源只有一个实例。

对象的创建/销毁过程最多只有一次,可以节省系统开销。

可以统一管理全局配置,如果单例模式的对象用来存储一些配置信息,可以实现对全局配置的管理。

简化了访问资源的入口,由于全局只有一个实例,使得客户端只需要关注一个访问入口即可。

 

单例模式的缺点:

缺乏灵活性,一旦创建了单例对象,就不能更改其实例化过程。

限制了一个类只能有一个实例,难以扩展该类的功能。

由于单例对象是全局可访问的,可能引发全局变量的滥用。

存在线程安全隐患,如果不增加一些锁机制,在多线程环境下可能会创建多个实例,影响单例的特性。

五、代码实战

#include <iostream>

using namespace std;

class Singleton
{
    private:
    static bool instanceFlag;
    static Singleton* single;
    Singleton()
    {
        printf("Private constructor finished.\n");
    }
    public:
    static Singleton* getInstance();
    void method();
    ~Singleton()
    {   
        printf("Public de-constructor finished.\n");
        instanceFlag = false;
    }
};

bool Singleton::instanceFlag = false;
Singleton* Singleton::single = NULL;

Singleton* Singleton::getInstance()
{
    if (!instanceFlag)
    {
        single = new Singleton();
        instanceFlag = true;
        return single;
    }
    else
    {
        return single;
    }
}

void Singleton::method()
{
    cout << "Method of the singleton class" << endl;
}

int main()
{
    Singleton* sc1, * sc2;

    sc1 = Singleton::getInstance();
    sc1->method();
    sc2 = Singleton::getInstance();
    sc2->method();

    delete sc1, sc2;
    return 0;
}

运行结果:

Private constructor finished.
Method of the singleton class
Method of the singleton class
Public de-constructor finished.

六、参考阅读

https://www.geeksforgeeks.org/singleton-pattern-c-design-patterns/

https://scottlilly.com/c-design-patterns-the-singleton-pattern/

https://www.tutorialspoint.com/explain-cplusplus-singleton-design-pattern

https://www.codeproject.com/Articles/1921/Singleton-Pattern-its-implementation-with-Cplusplu

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

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

相关文章

【Linux系统】—— 基本指令(三)

【Linux系统】—— 基本指令&#xff08;三&#xff09; 1 一切皆文件2 重定向操作2.1 初始重定向2.2 重定向的妙用2.3 追加重定向2.4 输入重定向2.5 一切皆文件与重定向结合 3 Linux 中的文件类型4 日志5 「more」命令6 「less」命令7 「head」与「tail」7.1 查看文件开头和结…

探索 Python 任务自动化的新境界:Invoke 库揭秘

文章目录 探索 Python 任务自动化的新境界&#xff1a;Invoke 库揭秘背景&#xff1a;为何选择 Invoke&#xff1f;什么是 Invoke&#xff1f;如何安装 Invoke&#xff1f;5个简单的库函数使用方法1. 定义任务2. 带参数的任务3. 运行 Shell 命令4. 任务参数化5. 列出任务 场景应…

【C++】list模拟实现(详解)

本篇来详细说一下list的模拟实现&#xff0c;list的大体框架实现会比较简单&#xff0c;难的是list的iterator的实现。我们模拟实现的是带哨兵位头结点的list。 1.准备工作 为了不和C库里面的list冲突&#xff0c;我们在实现的时候用命名空间隔开。 //list.h #pragma once #…

shell脚本(6)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;shell(6)if条件判断与for循环结构_哔哩哔哩_bilibili 本文主要讲解shell脚本中的if条件判断和for循环结构。 一、if语句 Shell 脚本中的 if 语句…

JavaScript基础 document.write()方法

JavaScript基础 document.write方法 1.简单认识document.write()2.document.write() 的使用 1.简单认识document.write() document.write() 是一种 JavaScript 方法&#xff0c;用于将内容直接写入到 HTML 文档中。它可以用来动态地在页面加载时插入文本、HTML 代码、图片等内…

Linux笔记---进程:进程切换与O(1)调度算法

1. 补充概念 1.1 并行与并发 竞争性&#xff1a;系统进程数目众多&#xff0c;而CPU资源只有少量&#xff0c;甚至只有1个&#xff0c;所以进程之间是具有竞争属性的。为了高效完成任务&#xff0c;更合理竞争相关资源&#xff0c;便具有了优先级。独立性&#xff1a;多进程运…

使用ENSP实现浮动静态路由

一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为1.1.1.1/24 ip address 1.1.1.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为2.…

GoF设计模式——结构型设计模式分析与应用

文章目录 UML图的结构主要表现为&#xff1a;继承&#xff08;抽象&#xff09;、关联 、组合或聚合 的三种关系。1. 继承&#xff08;抽象&#xff0c;泛化关系&#xff09;2. 关联3. 组合/聚合各种可能的配合&#xff1a;1. 关联后抽象2. 关联的集合3. 组合接口4. 递归聚合接…

【论文复现】深度知识追踪

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 深度知识追踪 1. 论文概述2. 论文方法3. 实验部分3.1 数据集3.2 实验步骤3.3 实验结果 4 关键代码 1. 论文概述 知识追踪的任务是对学生的知…

Linux: 进程地址空间(理解虚拟地址和页表)

目录 1. 虚拟地址 2. 进程地址空间分布 3. 描述进程地址空间 4. 内存管理——页表 5. 父子进程的虚拟地址关系 6. 页表标记位 6.1 读写权限 6.2 命中权限 7.为什么存在进程地址空间 1. 虚拟地址 #include <stdio.h> #include <unistd.h> #include <sy…

C语言:深入理解指针

一.内存和地址 我们知道计算机上CPU&#xff08;中央处理器&#xff09;在处理数据的时候&#xff0c;需要的数据是在内存中读取的&#xff0c;处理后的数据也会放回内存中&#xff0c;那我们买电脑的时候&#xff0c;电脑上内存是 8GB/16GB/32GB 等&#xff0c;那这些内存空间…

transformer.js(一):这个前端大模型运行框架的可运行环境、使用方式、代码示例以及适合与不适合的场景

随着大模型的广泛应用&#xff0c;越来越多的开发者希望在前端直接运行机器学习模型&#xff0c;从而减少对后端的依赖&#xff0c;并提升用户体验。Transformer.js 是一个专为前端环境设计的框架&#xff0c;它支持运行基于 Transformer 架构的深度学习模型&#xff0c;尤其是…

uni-app 发布媒介功能(自由选择媒介类型的内容) 设计

1.首先明确需求 我想做一个可以选择媒介的内容&#xff0c;来进行发布媒介的功能 &#xff08;媒介包含&#xff1a;图片、文本、视频&#xff09; 2.原型设计 发布-编辑界面 通过点击下方的加号&#xff0c;可以自由选择添加的媒介类型 但是因为预览中无法看到视频的效果&…

行业分析---2024年小鹏汽车AI Day及三季度财报

1 背景 在之前的博客中&#xff0c;笔者撰写了多篇行业类分析的文章&#xff08;科技新能源&#xff09;&#xff1a; 《行业分析---我眼中的Apple Inc.》 《行业分析---马斯克的Tesla》 《行业分析---造车新势力之蔚来汽车》 《行业分析---造车新势力之小鹏汽车》 《行业分析-…

数据可视化复习1-Matplotlib简介属性和创建子图

1.Matplotlib简介 Matplotlib是一个Python的2D绘图库&#xff0c;它可以在各种平台上以各种硬拷贝格式和交互环境生成具有出版品质的图形。通过Matplotlib&#xff0c;开发者可以仅需要几行代码&#xff0c;便可以生成绘图、直方图、功率谱、条形图、错误图、散点图等。 以下…

WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理

WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理 1. 标题识别elementUI组件爆红 这个原因是&#xff1a; 在官网说明里&#xff0c;才版本2024.1开始&#xff0c;默认启用的 Vue Language Server&#xff0c;但是在 Vue 2 项目…

如何安全删除 Linux 用户帐户和主目录 ?

Linux 以其健壮性和灵活性而闻名&#xff0c;是全球服务器和桌面的首选。管理用户帐户是系统管理的一个基本方面&#xff0c;包括创建、修改和删除用户帐户及其相关数据。本指南全面概述了如何在 Linux 中安全地删除用户帐户及其主目录&#xff0c;以确保系统的安全性和完整性。…

如何利用ros搭建虚拟场景通过仿真机器人完成一次简单的SLAM建图、导航规划(超简单)?——学习来源:机器人工匠阿杰

一&#xff1a;什么是SLAM&#xff0c;SLAM和导航规划又有什么关系&#xff1f; SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;即同时定位与建图&#xff09;是一种在未知或动态环境中自行驶的重要技术。主要通过设备上的传感器&#xff08;如激光雷达、…

shell脚本(完结)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;shell编程&#xff08;完结&#xff09;_哔哩哔哩_bilibili 本文主要讲解不同shell脚本中的相互调用以及输入输出重定向操作。 一、不同脚本之间…

禁用达梦DEM的agent

agent占用内存较多&#xff0c;实际没什么使用&#xff0c;考虑停止agent 应该切换到root执行停止 cd /dm/dmdbms/tool/dmagent/service/ ./DmAgentService stop禁用