重温设计模式--单例模式

文章目录

      • 单例模式(Singleton Pattern)概述
      • 单例模式的实现方式及代码示例
        • 1. 饿汉式单例(在程序启动时就创建实例)
        • 2. 懒汉式单例(在第一次使用时才创建实例)
      • 单例模式的注意事项
      • 应用场景
  • C++代码
      • 懒汉模式-经典版(线程不安全)
        • 经典版优化(线程安全)
      • 内部静态变量的懒汉实现
      • 饿汉模式

单例模式(Singleton Pattern)概述

  1. 定义
    单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。就像是在整个软件系统中,某个特定的对象只能有一个,并且各个部分都能方便地获取到这个唯一的对象。

  2. 作用

    • 资源共享与协调:适用于管理一些全局的资源,比如数据库连接池。整个应用程序通常只需要一个数据库连接池实例来协调和管理数据库连接的分配与回收,避免创建多个连接池导致资源浪费和管理混乱。
    • 状态一致性维护:在某些场景下,需要保证整个系统中某个对象的状态是唯一且一致的。例如,系统配置类,全局只有一份配置信息,各个模块获取的都是同一个配置实例,能保证配置的一致性,防止出现因多个不同配置实例而导致的逻辑混乱。
    • 节省内存和避免重复创建:对于一些创建成本较高或者占用较多系统资源的对象,只创建一个实例可以避免多次重复创建带来的内存消耗和性能开销,像一些复杂的日志记录类,创建实例可能涉及到初始化大量的文件操作相关资源等,单例模式可保证只创建一次。

在这里插入图片描述

单例模式的实现方式及代码示例

1. 饿汉式单例(在程序启动时就创建实例)
#include <iostream>// 饿汉式单例类
class Singleton {
private:// 将构造函数声明为私有,防止外部创建实例Singleton() {std::cout << "创建单例实例" << std::endl;}// 静态成员变量保存唯一实例,在程序启动时就初始化static Singleton* instance;
public:// 获取单例实例的静态方法static Singleton* getInstance() {return instance;}
};// 静态成员变量初始化,在程序启动时就创建好实例
Singleton* Singleton::instance = new Singleton;

以下是使用饿汉式单例的示例代码:

int main() {Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();// 比较两个指针,应该指向同一个实例if (s1 == s2) {std::cout << "s1和s2是同一个实例" << std::endl;}return 0;
}

在饿汉式单例中:

  • 优点:实现简单,线程安全(因为在程序启动时就完成了实例的创建,不存在多个线程同时创建实例的竞争问题),在多线程环境下也能保证只有一个实例被创建。
  • 缺点:如果单例类的构造函数执行一些比较耗时或者占用大量资源的初始化操作,并且这个单例可能在程序运行很久之后才会被用到,那么会造成程序启动时不必要的性能开销,提前占用了系统资源。
2. 懒汉式单例(在第一次使用时才创建实例)
#include <iostream>
#include <mutex>// 懒汉式单例类
class Singleton {
private:Singleton() {std::cout << "创建单例实例" << std::endl;}// 静态成员变量保存唯一实例指针static Singleton* instance;// 互斥锁用于保证多线程环境下的线程安全static std::mutex mutex_;
public:// 获取单例实例的静态方法,使用了双重检查锁定(DCLP)来优化线程安全和性能static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> guard(mutex_);if (instance == nullptr) {instance = new Singleton;}}return instance;}
};// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;

以下是使用懒汉式单例的示例代码:

int main() {Singleton* s1;Singleton* s2;// 模拟多线程环境下获取单例实例std::thread t1([&]() { s1 = Singleton::getInstance(); });std::thread t2([&]() { s2 = Singleton::getInstance(); });t1.join();t2.join();if (s1 == s2) {std::cout << "s1和s2是同一个实例" << std::endl;}return 0;
}

在懒汉式单例中:

  • 优点:实例在第一次使用时才创建,避免了程序启动时不必要的资源占用和性能开销,对于那些创建成本较高且可能不会马上用到的单例对象比较合适。
  • 缺点:实现相对复杂一些,需要考虑多线程环境下的线程安全问题,虽然使用了双重检查锁定等优化手段,但如果处理不当还是可能出现问题(比如内存乱序执行等情况,不过现代编译器和处理器一般会有相应机制来尽量避免)。

单例模式的注意事项

  1. 构造函数私有:无论是饿汉式还是懒汉式,都要将构造函数声明为私有,这样可以防止外部代码通过常规的方式(如Singleton s;这种直接实例化的语句)来创建多个实例,保证了单例的唯一性。
  2. 线程安全问题:在多线程环境下,懒汉式单例需要特别注意线程安全,要采用合适的同步机制(如互斥锁、原子操作等)来确保在多个线程同时尝试获取实例时,只有一个线程能够创建实例,避免创建出多个实例破坏单例模式的规则。而饿汉式单例天然具有一定的线程安全性,但也需要根据具体应用场景来考虑是否满足需求。
  3. 对象生命周期管理:要注意单例对象的生命周期,尤其是在动态内存分配(如new操作符创建实例)的情况下,需要合理地处理实例的释放,避免内存泄漏等问题。例如,可以通过定义一个静态的析构函数来释放单例对象占用的资源,但这需要谨慎设计,防止出现意外的行为。

单例模式在很多软件系统中都有广泛应用,不过也要根据实际情况合理选择合适的实现方式和注意相关的设计要点,以确保其能正确地发挥作用。

应用场景

  1. 系统配置管理:在一个应用程序中,通常会有各种配置信息,如数据库连接配置、服务器端口配置、应用程序的一些全局参数等。将这些配置信息封装在一个单例的配置类中,整个系统通过唯一的实例来获取和修改配置,保证了配置的一致性,并且方便统一管理。
  2. 日志记录器:用于记录程序运行过程中的各种日志信息,整个程序往往只需要一个日志记录实例来将日志输出到文件、控制台或者发送到远程日志服务器等。不同的模块都向这个唯一的日志记录器实例写入日志,确保日志管理的统一性和有序性。
  3. 线程池管理:在多线程编程中,线程池是管理和复用线程资源的重要组件。一般一个应用程序只需要一个线程池实例,通过这个单例的线程池来分配线程执行任务、回收线程等,避免创建多个线程池导致资源浪费和线程调度混乱。
  4. 缓存机制:例如网页缓存、数据库查询结果缓存等场景。以网页缓存为例,一个网站服务器可以有一个单例的缓存类,用于存储经常访问的网页内容,下次再有相同请求时可以直接从缓存中获取,减少服务器的响应时间和数据库等资源的消耗,而且只有一个缓存实例方便管理缓存的有效性、容量控制等。

C++代码

懒汉模式-经典版(线程不安全)

#include <iostream>
using namespace std;//懒汉模式
class Singleton
{
public:
/**
*需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例
*/static Singleton *GetInstance(){if (m_Instance == NULL ){m_Instance = new Singleton ();}return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}private:
/**
*构造函数卸载私有里,为了防止在外部调用类的构造函数而构造实例
*/Singleton();static Singleton *m_Instance;
};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance(); Singleton ::DestoryInstance();return 0;
}
经典版优化(线程安全)
#include <iostream>
using namespace std;//懒汉模式
class Singleton
{
public:
/*
此处进行了两次m_Instance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。
因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也
保证了线程安全。但是,这种实现方法在平时的项目开发中用的很好,也没有什么问题?但是,如果进行大数据
的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。
*/static Singleton *GetInstance(){if (m_Instance == NULL ){Lock(); if (m_Instance == NULL ){m_Instance = new Singleton ();}UnLock(); }return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}
private:Singleton();static Singleton *m_Instance;
};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();Singleton ::DestoryInstance();return 0;
}

内部静态变量的懒汉实现

此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。

#include <iostream>
using namespace std;class Singleton
{
public:static Singleton *GetInstance(){lock();static Singleton m_Instance;unlock();return &m_Instance;} 
private:Singleton();};int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;
}

饿汉模式

#include <iostream>
using namespace std;class Singleton
{
public:static Singleton *GetInstance(){return m_instace;} 
private:Singleton();static Singleton *m_instance;};
Singleton* Singleton :: m_instance = new Singleton();
int main(int argc , char *argv [])
{Singleton *singletonObj = Singleton ::GetInstance();singletonObj = Singleton ::GetInstance();return 0;
}

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

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

相关文章

Java字符串的|分隔符转List实现方案

字符串处理 问题背景代码实现代码优化原因分析实现方案 注意事项异常处理Maven未识别异常 问题背景 在项目组对账流程中&#xff0c;接收对方系统的对账文件&#xff0c;数据以|为分隔符&#xff0c;读取文件内容&#xff0c;分条入库。 代码实现 Java中将字符串转给list&am…

clickhouse复现修复 结构需要清理 错误 structure need clean

在工作过程中&#xff0c;偶然发现clickhouse有时会出现数据目录结构需要清理的问题&#xff0c;但是很难复现&#xff0c;经过不断尝试&#xff0c;将clickhouse安装在设备上&#xff0c;然后写了一个程序持续插入clickhouse数据&#xff0c;然后突然拔掉设备的电源&#xff0…

项目底链华为链切换长安链经验总结

项目底链华为链切换长安链经验总结 前言业务需求分析智能合约重写k-v存储结构设计设计上链存储的结构体使用迭代器查询历史记录长安链合约编辑器历史记录返回错误材料上链非必传字段 Int 类型自动赋值长安链cmc工具部署合约ca证书需齐全分页查询截取处理&#xff0c;返回 nil处…

【机器学习】从流动到恒常,无穷中归一:积分的数学诗意

文章目录 微积分基础&#xff1a;理解变化与累积的数学前言一、积分概述与基础概念1.1 积分的定义与重要性1.1.1 积分的基本组成1.1.2 积分在机器学习中的应用 1.2 积分的历史与发展 二、积分的基本概念与计算2.1 不定积分2.1.1 不定积分的定义2.1.2 不定积分的计算方法2.1.3 实…

在瑞芯微RK3588平台上使用RKNN部署YOLOv8Pose模型的C++实战指南

在人工智能和计算机视觉领域,人体姿态估计是一项极具挑战性的任务,它对于理解人类行为、增强人机交互等方面具有重要意义。YOLOv8Pose作为YOLO系列中的新成员,以其高效和准确性在人体姿态估计任务中脱颖而出。本文将详细介绍如何在瑞芯微RK3588平台上,使用RKNN(Rockchip N…

电磁兼容(EMC):一文解读磁芯复合材料——塑磁

目录 01 塑磁的定义 02 塑磁的常见规格型号 03 塑磁材料的优点 04 塑磁的应用 塑磁,也称为注塑磁,是一种将磁性粉末注入到塑料基体中制成的复合磁体材料。以下是塑磁的定义、应用和材料特性的总结: 01 塑磁的定义 塑磁是以塑料为基体,通过特殊工艺在其中加入磁性粒子(…

五种msvcr100.dll丢失的解决方法,有效修复msvcr100.dll丢失错误!跟msvcr100.dll错误问题说拜拜!

在日常电脑使用过程中&#xff0c;尤其是运行某些应用程序或游戏时&#xff0c;可能会遇到“msvcr100.dll丢失”的错误提示。这个动态链接库&#xff08;DLL&#xff09;文件是Microsoft Visual C Redistributable for Visual Studio 2010的一部分&#xff0c;对于许多程序的正…

xdoj 数字个数统计

1-3 数字个数统计 3 时间限制&#xff1a;1S 题目描述&#xff1a; 输入两个三位正整数 A 和 B&#xff0c;在区间[A,B]之间&#xff0c;或在区间[B,A]之间&#xff0c; 完成统计任务&#xff1a; 3 的倍数数字个数、4 的倍数数字个数和 5 的倍数且不是 2 的倍数 的数字个数…

redis数据类型:list

数据结构 源码版本&#xff1a;7.2.2路径&#xff1a;src/adlist.h 关于list的 头文件中涉及到的这三个结构体如下 /* Node, List, and Iterator are the only data structures used currently. */ # 节点 typedef struct listNode {struct listNode *prev; # 前元素的指针s…

计算机网络(网络层)

1、ARP协议(已知ip地址得到mac地址) 在传输一个 IP 数据报的时候&#xff0c;确定了源 IP 地址和目标 IP 地址后&#xff0c;就会 通过主机「路由表」确定 IP 数据包下一跳。然而&#xff0c;网络层的下一层是数据链路层&#xff0c;所以我们还要知道「下一跳」的 MAC 地址。由…

springboot容器无法获取@Autowired对象,报null对象空指针问题的解决方式

示例错误代码&#xff1a; package com.uniin.ib.provider.iot.handle;Slf4j Component public class FireStringInboundHandler extends ChannelInboundHandlerAdapter {Autowiredprivate RsFireMonitoringMapper rsFireMonitoringMapper;Autowiredprivate RsFireAlertMapper…

5G CPE 主控CPU(上位机) 数据转发性能评估--基于5G模组和NEO3开发板评估

5G CPE ARM 主控CPU 数据转发性能评估--基于5G模组和NEO3开发板评估 摘要NEO3介绍演示架构网络拓扑硬件连接固件及驱动NEO固件NEO3串口驱动拔号及网络配置配置AT接口拔号NEO3网络接口性能测试5G空口性能NEO千兆网口性能验证NEO3 高速接口数据转发性能摘要 当前主流CPE多采用集…

Linux下学【MySQL】表中插入和查询的进阶操作(配实操图和SQL语句通俗易懂)

绪论​ 每日激励&#xff1a;挫折是会让我们变得越来越强大的重点是我们敢于积极的面对它。—Jack叔叔 绪论​&#xff1a; 本章是表操作的进阶篇章&#xff08;没看过入门的这里是传送门&#xff0c;本章将带你进阶的去学习表的插入insert和查找select&#xff0c;本质也就是…

[Rust开发]actix_webmiddleware 中间件

actix_web::middleware 在 Actix Web 框架中扮演着重要的角色&#xff0c;它允许开发者在处理 HTTP 请求和响应的过程中插入自定义的逻辑。中间件可以在请求到达处理函数之前或响应返回给客户端之前执行&#xff0c;从而实现日志记录、身份验证、数据验证、错误处理等功能。 为…

WebLogic T3反序列化漏洞(CVE-2018-2628)--vulhub

WebLogic T3反序列化漏洞(CVE-2018-2628) WebLogic在通信过程中使用T3协议传输数据&#xff0c;涉及到了序列化和反序列化操作。 T3协议概述 T3协议是Oracle的私有协议&#xff0c;所以公开的相关资料比较少&#xff0c;这里结合其他师傅的博客简单对T3协议进行一个简要分析…

redis和mysql的区别

Redis是一种非关系型数据库&#xff08;NoSQL&#xff09;&#xff0c;将数据存储在缓存中&#xff0c;这虽然提高了运行效率&#xff0c;但是保存时间却很短。将数据存储在内存中&#xff0c;因此读写速度非常快&#xff0c;特别适合用于需要高速访问的场景&#xff0c;如缓存…

OpenFeign快速入门 示例:黑马商城

使用起因 之前我们利用了Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。这样一来购物车虽然通过远程调用实现了调用商品服务的方法,但是远程调用的代码太复杂了: 解决方法 并且这种调用方式比较复杂&#xff0c;一会儿远程调用&#xff0c;一会儿本地调用。 因…

计算机的错误计算(一百九十)

摘要 用两个大模型计算cot(1.234). 其中&#xff0c;1.234是以弧度为单位的角度。结果保留10位有效数字。实验表明&#xff0c;两个的计算公式虽然不同&#xff0c;但是都是正确的。然而&#xff0c;数值计算则是有问题的---包括每一个中间运算与结果。 例1. 计算cot(1.234)…

【QSS样式表 - ⑥】:QPushButton控件样式

文章目录 QPushBUtton控件样式QSS示例 QPushBUtton控件样式 常用子控件 常用伪状态 QSS示例 代码&#xff1a; QPushButton {background-color: #99B5D1;color: white;font-weigth: bold;border-radius: 20px; }QPushButton:hover {background-color: red; }QPushButton:p…

深度学习与图像处理(国产深度学习框架——飞桨官方指定教材)

计算机视觉从小白到大师之路 《深度学习与图像处理&#xff08;PaddlePaddle版&#xff09;》这一本就够了 1.引言 随着人工智能技术的飞速发展&#xff0c;各行各业对深度学习、图像处理相关领域的人才需求日益迫切。本书旨在通过系统的理论讲解与丰富的实战案例&#xff0…