单例模式 (Singleton Pattern)

devtools/2025/3/19 15:16:18/

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

一、基础

1. 意图

  • 确保一个类只有一个实例。
  • 提供一个全局访问点。

2. 适用场景

  • 一个类只需要一个实例来协调系统行为时,例如数据库连接池,线程池、缓存、日志对象等。
  • 需要控制实例数目,节省系统资源,避免重复创建和浪费, 同时保证数据的一致性和正确性。

3. 结构

  • 一个静态成员变量
  • 一个私有构造函数
  • 一个静态方法

二、进阶

1. 线程安全

  • 饿汉式: 在类加载时就创建实例,线程安全,但可能会造成资源浪费。
  • 懒汉式: 在第一次调用 getInstance() 时才创建实例,需要考虑线程安全问题。
  • 双重检查锁定: 使用 volatile 关键字和 synchronized 关键字来保证线程安全。
  • 静态内部类: 利用类加载机制保证线程安全。

2. 序列化与反序列化

  • 在 C++ 中,并没有像 Java 那样内置的序列化机制。但是,如果使用第三方库(如 Boost.Serialization)进行序列化和反序列化操作,同样需要注意单例模式的问题。在反序列化时,可能会创建新的实例对象,破坏单例模式。为了避免这种情况,可以在反序列化过程中进行特殊处理,确保返回的是已经存在的单例实例。

3. 反射攻击

  • 为了防止反射调用私有构造函数创建新的实例,可以在构造函数中进行判断。

三、关键点

1. 私有构造函数:单例类的构造函数必须是私有的,以防止外部通过new关键字实例化对象,确保只有一个实例对象存在。

2. 静态实例变量:单例类中必须有一个静态的实例变量来保存唯一的实例对象,并且该实例变量的作用域必须是私有的,以防止外部直接访问和修改。

3. 公共静态访问方法:提供一个公共的静态方法,作为全局访问点,用于获取单例对象的实例。该方法通常命名为getInstance()。

4. 线程安全:在多线程环境下,单例模式的实现必须保证线程安全,确保在任何情况下都只会创建一个实例对象。常见的线程安全实现方式有同步方法、双重检查锁定、静态局部变量等。

5. 延迟加载:对于某些应用场景,延迟加载(懒汉式)是非常重要的,它可以避免在程序启动时就创建不必要的实例对象,从而提高系统性能和资源利用率。但需要注意的是,懒汉式实现需要处理好线程安全问题。

四、易错点

1. 线程安全问题:在实现懒汉式单例模式时,如果不注意线程安全,很容易导致在多线程环.          境下创建多个实例对象,违反单例模式的定义。例如,在没有同步机制的情况下,多个线程同时.   调用getInstance()方法,并且此时instance为nullptr,就会创建多个实例对象。

2. 序列化与反序列化:当使用第三方库进行序列化和反序列化操作时,如果不处理好单例模式的问题,可能会导致在反序列化时创建新的实例对象,破坏单例模式。需要在反序列化过程中进行特殊处理,确保返回的是已经存在的单例实例。

3. 反射破坏:虽然 C++ 没有像 Java 那样强大的反射机制,但是通过一些技术手段也可以实现类似反射的功能。如果不小心使用这些技术调用了单例类的私有构造函数,同样会破坏单例模式。为了防止这种情况,需要在构造函数中添加逻辑判断,防止重复创建实例对象。

4. 指令重排:在使用双重检查锁定实现线程安全的懒汉式单例模式时,需要注意编译器和处理器的指令重排问题。虽然 C++11 标准中引入了内存模型来解决这个问题,但是在一些老版本的编译器或者特定的硬件平台上,仍然可能存在指令重排导致的线程安全问题。

五、核心代码

1. 饿汉式

饿汉式是单例模式中最简单的一种实现方式。在程序启动时,就立即创建唯一的实例对象,并且该实例对象是静态的,因此在程序的整个生命周期中只会存在一个实例。

优点:实现简单,线程安全,因为在程序启动时就创建了实例,不存在多线程并发创建的问题。

缺点:如果该单例对象在整个应用程序中不一定会被使用到,那么在程序启动时就创建实例可能会造成资源浪费。

class Singleton {
private:static Singleton* instance;Singleton() {} // 私有构造函数public:static Singleton* getInstance() {return instance;}
};Singleton* Singleton::instance = new Singleton(); // 类加载时创建实例

2. 懒汉式 (双重检查锁定)

双重检查锁定(Double-Checked Locking)是一种优化的线程安全懒汉式实现方式。它通过两次检查instance是否为nullptr,在保证线程安全的同时提高了性能。

优点:延迟加载,只有在真正需要使用单例对象时才创建,避免了不必要的资源浪费。既实现了线程安全,又提高了性能。在第一次检查instance为nullptr时,只有一个线程能够进入同步块,在同步块内再次检查instance为nullptr时才创建实例对象,避免了每次调用getInstance()方法都获取锁的性能开销。

缺点:实现相对复杂,需要理解双重检查锁定的原理以及互斥锁的使用。

class Singleton {
private:static Singleton* instance;static std::mutex mtx;Singleton() {} // 私有构造函数public:static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mtx);if (instance == nullptr) {instance = new Singleton();}}return instance;}
};Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

3. 懒汉式 (静态内部类)

在 C++11 之后,可以利用静态局部变量的特性来实现单例模式,这种方式既实现了延迟加载,又保证了线程安全。

优点:延迟加载,线程安全。由于静态局部变量在第一次调用getInstance()方法时才会被初始化,因此实现了延迟加载。同时,C++11 标准保证了静态局部变量的初始化是线程安全的。

缺点:无明显缺点。

class Singleton {
private:Singleton() {} // 私有构造函数static class SingletonHolder {public:static Singleton* instance;};public:static Singleton* getInstance() {return SingletonHolder::instance;}
};

4. 防止序列化与反序列化

class Singleton {
private:static Singleton* instance;Singleton() {} // 私有构造函数public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}// 防止反序列化创建新的实例Singleton* readResolve() {return getInstance();}
};Singleton* Singleton::instance = nullptr;

5. 防止反射攻击

class Singleton {
private:static Singleton* instance;Singleton() {if (instance != nullptr) {throw std::runtime_error("Singleton instance already exists!");}} // 私有构造函数public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}
};Singleton* Singleton::instance = nullptr;

六、总结

单例模式是一种常用的设计模式,但需要注意线程安全、序列化与反序列化、反射攻击等问题。在实际开发中,需要根据具体需求选择合适的实现方式。


http://www.ppmy.cn/devtools/168370.html

相关文章

实验9-2 高级搜索技术2

实验9-2 高级搜索技术2 一、实验目的 &#xff08;1&#xff09;掌握高级搜索技术的相关理论&#xff0c;能根据实际情况选取合适的搜索方法&#xff1b; &#xff08;2&#xff09;掌握遗传算法的基本思想&#xff0c;能根据实际问题选择种群数量、选择方法、交叉与变异方法&…

C++20 新特性全面解析:从概念到协程的编程革命

一、引言&#xff1a;C20的里程碑意义 2020年发布的C20标准被公认为继C11之后最重要的版本更新&#xff0c;带来了4大核心特性和20项重大改进。这些变革不仅提升了代码表达力&#xff0c;更从根本上改变了C的编程范式。本文将深入解析C20的关键特性&#xff0c;并通过实战代码…

卷积神经网络(CNN)与反向传播

卷积神经网络&#xff08;CNN&#xff09;是一种专门用于处理图像数据的深度学习模型&#xff0c;它的工作原理可以简单理解为以下几步&#xff1a;1. 输入层&#xff1a;首先&#xff0c;把图像输入到网络中。图像在计算机里是以数字矩阵的形式存储的&#xff0c;比如一个灰度…

AI入门7:python三种API方式调用本地Ollama+DeepSeek

回顾 书接上篇&#xff1a;各种方式搭建了本地知识库&#xff1a; AI入门&#xff1a;AI模型管家婆ollama的安装和使用-CSDN博客 AI入门2&#xff1a;本地AI部署&#xff0c;用ollama部署deepseek&#xff08;私有化部署&#xff09;-CSDN博客 AI入门3&#xff1a;给本地d…

AK 接口

文章目录 前言AK接口简介帧结构速度脉冲数据段 位编码运行状态低速状态高速模式 时序参数 IP 设计结构框图接口设计上板验证 前言 本文参考KMI25/2产品手册&#xff08;High performance rotational speed sensor&#xff09; AK接口 简介 AK协议是一种轮速传感器&#xff…

性能测试之grafana展示jmeter测试指标与主机监控

性能测试之grafana展示jmeter测试指标与主机监控 背景 ​ 公司新的项目准备开展性能测试,之前性能监控主要使用的jmeter的插件jpgc-Transactions per Second 与 jpgc- Response Times Over Time 与 jpgc - Active Threads Over Time等等插件监控性能指标结果,PerfMon Metrics…

数据库管理-第303期 数据库相关硬件文章汇总(20250319)

数据库管理303期 2025-03-19 数据库管理-第303期 数据库相关硬件文章汇总&#xff08;20250319&#xff09;1 CPU & 内存2 SSD3 RDMA4 存储5 CXL6 硬件采购7 数据库一体机总结 数据库管理-第303期 数据库相关硬件文章汇总&#xff08;20250319&#xff09; 作者&#xff1…

第六章-PHP错误处理

PHP错误处理 一&#xff0c;错误处理的基本概念&#xff1a; 1. 错误类型 PHP中的错误主要分为以下几类&#xff1a; 致命错误 (Fatal Errors): 这些错误会导致脚本终止执行。例如&#xff0c;调用未定义的函数或类。警告 (Warnings): 这些错误不会终止脚本执行&#xff0c…