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

一、单例模式的定义  

单例模式,英文全称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

C++完美实现Singleton模式 立即下载

相关推荐

计算机组成原理实验二 运算器组成实验

事实上,ALU的设计是在全加器的基础上,对全加器功能的扩展来实现符合要求的多种算术/逻辑运算的功能。为了实现多种功能的运算,An、Bn数据是不能直接与全加器相连接的,它们受到功能变量F3—F1的制约,因此,可由An、Bn数据和功能变量F3-F1组合成新的函数Xn 、Yn,然后,再将Xn 、Yn和下一位进位Cn-1通过全加器进行全加运算以实现所需的运算功能。AC寄存器可用来存储ALU的输入以实现两个操作数的运算操作,也可以用来存储来自ALU的结果,以将这些结果反馈给ALU以进行更多的处理。

qq_63574533的博客 1449

Flink项目基础配置指南

在大数据处理领域,Apache Flink凭借强大的实时流处理和批处理能力,成为众多开发者的首选工具。在日常工作中,开发Flink Jar任务是常见需求,但每次都需重复配置日志、梳理pom依赖、设置打包插件等,流程繁琐且易出错。为提升开发效率,减少重复劳动,将这些基础配置进行整理归纳十分必要。本文将围绕Flink项目的本地日志配置、pom依赖及插件配置展开详细介绍,为开发者提供一套可直接复用的基础配置方案,助力Flink项目高效开发。在Flink项目中,日志是了解任务运行状态、排查问题的重要依据。本地日志配

qq_42773076的博客 1210

产品经理必看!用Cursor自动生成符合开发规范的技术文档(含UI设计规则模板)

本文详细介绍了产品经理如何利用Cursor工具自动生成符合开发规范的技术文档,并整合UI设计规则模板。通过配置标准化文档结构和设计关联规则,Cursor能有效解决术语不统一、结构混乱等问题,提升跨团队协作效率。文章包含实际案例、规则配置要点及企业级实施方案,帮助产品经理快速掌握AI辅助文档生成技巧。

emacs5lisp的博客 223

深度学习 tesorflow2.0版本 入门第一章

为什么要学tensflow 深度学习发展迅猛 框架灵活 google开发 公司应用广泛 2.0更加灵活 后面会学习的项目 图像分类 房价预测 泰坦尼克号生存预测 文本分类 文本生成 机器翻译 核心部分 模型训练 tesorboard----> 查看模型训练进度 模型保存和部署 分布式训练-----> 加速训练速度,提升训练效率 TensorFlow常用库:Tfds, tfhub,tensor2tensor 多平台云端环境GPU:Goolgle cloud环境配置,AWS环境配置 kaggle平

weixin_46969441的博客 1236

VMware vSphere运维管理手册

ESXi 7.0 引入了一种系统存储布局,可对大型模块和第三方组件进行灵活的分区管理和支持,同时简化了调试操作。ESXi 7.0 系统存储更改在 ESXi 7.0 之前,分区大小固定不变,除 /scratch 分区和可选 VMFS 数据存储之外,且分区号是静态的,这些限制了分区管理。在 ESXi 7.0 中,分区整合为更少且更大的可扩展分区,具体取决于所使用的引导介质及其容量。ESXi 7.0 系统存储布局包含四个分区:分区适用情况类型系统引导存储引导加载程序和 EFI 模块。FAT16。

Avenjan优快云博客 3624

PySWMM新手避坑指南:从安装到批量处理INP文件的完整流程(附常见错误解决方案)

本文为PySWMM新手提供从环境搭建到批量处理INP文件的完整避坑指南。详细讲解了虚拟环境配置、库安装方法,并针对“无法打开输入文件”和对象访问错误等高频问题提供解决方案。核心部分展示了如何利用Python脚本实现INP文件的批量运行、结果汇总以及参数自动化修改,显著提升SWMM模型的工作效率。

weixin_29092031的博客 393

ThingsBoard PE专业版解决方案技术文档——温度&湿度

温湿度解决方案

weixin_67440240的博客 818

【数据集】30 m地表温度LST数据集

【数据集】30 m地表温度LST数据集

WW、forever的博客 1127

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

上一篇: C++设计模式——Abstract Factory Pattern抽象工厂模式
下一篇: C++编程库与框架实战——sqlite3数据库
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员与背包客_CoderZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值