当前位置: 首页 > news >正文

《C++设计模式》单例模式

文章目录

  • 1、简介
  • 2、单例模式的种类
    • 2.1 饿汉式单例模式:
    • 2.2 懒汉式单例模式:
  • 3、单例模式的具体介绍
    • 3.1、饿汉式
      • 3.1.1、代码示例
      • 3.1.2、组成部分
      • 3.1.3、优缺点
      • 3.1.4、应用场景
    • 3.2、懒汉式
      • 3.2.1、代码示例
      • 3.2.2、组成部分
      • 3.2.3、优缺点
      • 3.2.4、应用场景
  • 4、面试常问问题
  • 5、总结
  • 6、参考文章

1、简介

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这个模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
以下是单例模式的一些关键点
(1)私有构造方法:为了防止外部代码通过new关键字创建类的多个实例,单例模式的构造方法是私有的。
(2)静态变量:类的唯一实例通常存储在一个静态变量中,这样它可以被类的所有实例共享和访问。
(3)公共静态方法:为了提供对唯一实例的全局访问,单例模式通常包含一个公共的静态方法,该方法在需要时返回类的唯一实例。如果实例尚不存在,该方法会创建它;如果实例已经存在,则直接返回该实例。
(4)线程安全:在多线程环境中,需要确保单例模式的实现是线程安全的,以防止多个线程同时创建类的多个实例。这通常通过同步机制来实现。

2、单例模式的种类

2.1 饿汉式单例模式:

(1)在类加载时就创建实例。
(2)线程安全,因为实例在类加载时就已经存在,不存在多线程竞争问题。
(3)可能导致内存浪费,因为即使从未使用过该实例,它仍然会被创建。
(4)实现简单,通常通过静态变量和静态代码块来完成。

2.2 懒汉式单例模式:

(1)在第一次使用时才创建实例。
(2)非线程安全,因为多个线程可能同时进入创建实例的代码块,导致创建多个实例(除非添加额外的同步机制)。
(3)节省内存资源,因为实例只在需要时才创建。
(4)实现相对复杂,需要处理线程安全问题,通常通过同步方法或双重检查锁定来实现。

3、单例模式的具体介绍

3.1、饿汉式

3.1.1、代码示例

#include <iostream>class Singleton {
public:// 禁用拷贝构造函数和赋值运算符,确保单例的唯一性Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 提供一个公共的静态方法来获取单例对象static Singleton& getInstance() {static Singleton instance; // 静态局部变量,只在第一次调用时构造return instance;}// 一个示例方法,用于展示单例对象的使用void doSomething() {std::cout << "Doing something in Singleton instance!" << std::endl;}private:// 私有构造函数,防止外部直接创建实例Singleton() {std::cout << "Singleton instance created!" << std::endl;}// 禁用析构函数,防止外部调用delete(实际上在程序结束时会自动调用)// 注意:对于饿汉式单例,析构函数通常不需要特别处理,因为实例会在程序结束时自动销毁// 但为了完整性,这里仍然声明为私有,并添加注释~Singleton() = default;
};int main() {// 获取单例对象并调用其方法Singleton& singleton = Singleton::getInstance();singleton.doSomething();// 尝试获取同一个单例对象(实际上仍然是同一个实例)Singleton& anotherSingleton = Singleton::getInstance();anotherSingleton.doSomething();// 由于拷贝构造函数和赋值运算符被禁用,以下代码会导致编译错误// Singleton copySingleton = Singleton::getInstance(); // 错误:拷贝构造函数被删除// Singleton assignSingleton;// assignSingleton = Singleton::getInstance(); // 错误:赋值运算符被删除return 0;
}

运行结果:

Singleton instance created!
Doing something in Singleton instance!
Doing something in Singleton instance!

3.1.2、组成部分

(1)私有静态成员变量:
这是一个静态的类成员变量,用于存储单例实例。由于它是私有的,所以外部类不能直接访问或修改它。
(2)私有构造函数:
类的构造函数被声明为私有,以防止外部类通过new关键字创建新的实例。这是实现单例模式的关键之一。
(3)公共静态方法:
提供一个公共的静态方法(通常是getInstance),用于返回单例实例。这个方法检查静态成员变量是否已经持有实例,如果还没有,则创建一个新的实例并返回;如果已经存在,则直接返回该实例。
(4)静态初始化块(可选):
在某些情况下,单例实例的初始化可能需要执行一些复杂的逻辑,这时可以使用静态初始化块来完成。然而,在饿汉模式中,由于实例在类加载时就已经被创建,所以通常不需要静态初始化块。

3.1.3、优缺点

(1) 优点:线程安全,实现简单。
(2) 缺点:可能导致内存浪费,因为即使从未使用过该实例,它仍然会被创建。

3.1.4、应用场景

(1)线程安全需求高:由于饿汉模式在类加载时就创建了实例,所以它是线程安全的,无需额外的同步机制。这在多线程环境中尤其有用,可以避免竞争条件和潜在的线程安全问题。
(2)实例创建开销小:如果实例的创建开销很小,或者实例的创建和初始化过程不会消耗太多资源,那么饿汉模式是一个很好的选择。因为即使实例在类加载时被创建,也不会对系统性能产生显著影响。
(3)实例需要提前准备:在某些情况下,实例需要在类加载时就准备好,以便在后续的代码执行过程中随时使用。例如,某些配置信息或资源需要在应用程序启动时就被加载和初始化。

3.2、懒汉式

3.2.1、代码示例

#include <iostream>
#include <mutex>
#include <memory>class Singleton {
public:// 禁用拷贝构造函数和赋值运算符Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 获取单例实例的静态方法static Singleton* getInstance() {// 使用双重检查锁定(Double-Checked Locking)来确保线程安全和性能if (instance_ == nullptr) {std::lock_guard<std::mutex> lock(mutex_);if (instance_ == nullptr) {instance_ = new Singleton();}}return instance_;}// 示例方法void doSomething() {std::cout << "Singleton instance is doing something!" << std::endl;}// 析构函数,用于清理资源~Singleton() {std::cout << "Singleton instance is being destroyed!" << std::endl;}private:// 私有构造函数,防止外部创建实例Singleton() {std::cout << "Singleton instance is being created!" << std::endl;}// 静态成员变量,保存单例实例static Singleton* instance_;// 静态互斥锁,用于线程同步static std::mutex mutex_;
};// 初始化静态成员变量
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;int main() {// 获取单例实例并调用示例方法Singleton* singleton1 = Singleton::getInstance();singleton1->doSomething();// 再次获取单例实例(应该是同一个实例)Singleton* singleton2 = Singleton::getInstance();singleton2->doSomething();// 注意:main函数结束时,单例实例的析构函数会被调用return 0;
}

3.2.2、组成部分

(1)私有的构造函数
懒汉式单例模式通过将构造函数私有化,防止外部通过new关键字直接创建多个实例。这是实现单例模式的关键一步,因为如果没有私有化构造函数,外部代码就可以随意创建类的实例,从而破坏单例模式的规则。
(2) 私有的静态实例变量
懒汉式单例模式使用一个私有的静态实例变量来存储唯一的实例。这个变量在类加载时不会被初始化,而是在第一次调用获取实例的方法时才会被创建。由于该变量是静态的,因此它只会在类的整个生命周期中存在一个实例。
(3) 公共的静态方法
懒汉式单例模式通过提供一个公共的静态方法来获取唯一的实例。这个方法首先会检查私有的静态实例变量是否为null,如果是,则创建一个新的实例并将其赋值给该变量。如果实例已经存在,则直接返回该实例。
(4)线程安全机制(可选)
在多线程环境下,懒汉式单例模式可能会出现多个线程同时创建实例的情况。为了解决这个问题,可以在获取实例的方法中添加同步机制,如使用synchronized关键字或java.util.concurrent包中的锁机制来确保只有一个线程能够创建实例。

3.2.3、优缺点

(1) 优点:节省内存资源,因为实例只在需要时才创建。
(2) 缺点:非线程安全,因为多个线程可能同时进入创建实例的代码块,导致创建多个实例。需要额外的同步机制来保证线程安全。

3.2.4、应用场景

(1)延迟加载:如果实例的创建开销较大,或者实例的创建和初始化过程会消耗较多资源,那么懒汉模式可以通过延迟加载来节省资源。只有在实际需要使用时,才会创建和初始化实例。
(2)节省内存:在某些情况下,如果实例在类加载时并不需要使用,那么懒汉模式可以避免不必要的内存占用。只有在需要时,才会分配内存和创建实例。
(3)实例创建依赖条件:如果实例的创建依赖于某些条件或参数,并且这些条件或参数在类加载时并不确定,那么懒汉模式可以根据实际情况来创建实例。这提供了更大的灵活性和适应性。

4、面试常问问题

对于C++的单例模式,面试高频问题主要集中在单例模式的基本概念、应用场景、实现方式及其相关细节上。以下是一些可能的面试问题及简要解答:
一、单例模式的基本概念
(1)什么是C++中的单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在C++中,这通常通过私有化构造函数、拷贝构造函数和赋值运算符,并提供一个静态的公共方法来获取实例来实现。
(2)单例模式的主要目的是什么?
单例模式的主要目的是确保整个进程中,只有一个类的实例,并且提供一个统一的访问接口。这通常用于需要全局访问并共享资源的场景,如日志记录器、配置管理器和数据库连接器等。
二、单例模式的应用场景
(1)请列举一些C++中单例模式的应用场景?
日志记录器:确保整个应用程序中只有一个日志记录器实例,以便集中管理日志输出。
配置管理器:用于读取和管理应用程序的配置信息,确保配置信息的一致性和全局可访问性。
数据库连接器:在需要连接数据库时,确保只有一个数据库连接器实例,以减少资源消耗和提高性能。
三、单例模式的实现方式
(1)C++中实现单例模式有哪些常见的方法?
饿汉式:在类加载时就初始化实例,线程安全但可能会浪费空间。
懒汉式:在第一次调用时才初始化实例,实现了懒加载,但需要注意线程安全问题。可以通过加锁或使用双重检查锁来确保线程安全。
静态内部类实现:利用C++11中的局部静态变量特性,在第一次调用时初始化实例,线程安全且效率高。
(2)什么是双重检查锁(DCL)?它在C++单例模式中的作用是什么?
双重检查锁是一种优化技术,用于在懒汉式单例模式中确保线程安全的同时提高效率。它首先检查实例是否已经存在,如果不存在则加锁,然后再次检查实例是否存在(以避免在加锁期间其他线程已经创建了实例),最后创建实例。这样可以减少不必要的加锁操作,提高性能。
(3)为什么C++11中的局部静态变量可以用于实现线程安全的单例模式?
C++11标准保证了局部静态变量的初始化是线程安全的。因此,在静态方法中利用局部静态变量来初始化单例实例,可以确保在多线程环境中只有一个线程能够创建实例,而其他线程将直接获取到已经创建的实例。
四、单例模式的进阶问题
(1)如何防止C++中的单例模式被反射攻击?
在C++中,由于不存在像Java那样的反射机制,因此通常不需要特别考虑防止反射攻击的问题。但是,如果使用了某些支持反射的库或框架,则需要通过适当的措施来防止反射攻击,如私有化构造函数和析构函数等。
(2)如何确保C++中的单例模式在序列化和反序列化过程中保持唯一性?
在C++中,如果单例类需要被序列化,则需要实现自定义的序列化逻辑。在反序列化过程中,可以检查是否已经存在实例,如果存在则直接返回该实例而不是创建新实例。这通常需要在序列化时保存一些额外的信息来标识实例的唯一性。
(3)C++中的单例模式是否可以被删除或重置?
在大多数情况下,单例模式不需要被删除或重置。因为单例实例通常在整个应用程序的生命周期内都存在,并且被多个模块或组件共享。然而,在某些特殊情况下(如单元测试或应用程序重置等),可能需要提供一种机制来删除或重置单例实例。这可以通过在单例类中添加一个专用的销毁函数来实现,但需要注意线程安全和资源释放等问题。

5、总结

单例模式是一种简单而有效的设计模式,但在使用时需要注意其优缺点和注意事项,以确保其正确性和高效性。

6、参考文章

本文章由文心一言搜索的来的。

相关文章:

《C++设计模式》单例模式

文章目录 1、简介2、单例模式的种类2.1 饿汉式单例模式&#xff1a;2.2 懒汉式单例模式&#xff1a; 3、单例模式的具体介绍3.1、饿汉式3.1.1、代码示例3.1.2、组成部分3.1.3、优缺点3.1.4、应用场景 3.2、懒汉式3.2.1、代码示例3.2.2、组成部分3.2.3、优缺点3.2.4、应用场景 4…...

mapbox进阶,添加路径规划控件

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️MapboxDirections 控件二、🍀添加路径规划控件1. ☘️实现思路2. ☘️…...

【论文阅读笔记】SCI算法与代码 | 低照度图像增强 | 2022.4.21

目录 一 SCI 1 SCI网络结构 核心代码&#xff08;model.py&#xff09; 2 SCI损失函数 核心代码&#xff08;loss.py&#xff09; 3 实验 二 SCI效果 1 下载代码 2 运行 一 SCI &#x1f49c;论文题目&#xff1a;Toward Fast, Flexible, and Robust Low-Light Image …...

RAG实战:本地部署ragflow+ollama(linux)

1.部署ragflow 1.1安装配置docker 因为ragflow需要诸如elasticsearch、mysql、redis等一系列三方依赖&#xff0c;所以用docker是最简便的方法。 docker安装可参考Linux安装Docker完整教程&#xff0c;安装后修改docker配置如下&#xff1a; vim /etc/docker/daemon.json {…...

前路漫漫,曙光在望 !

起始 从20年大一开始写作至今&#xff0c;转眼五年时光已经过去了&#xff0c;最开始在CSDN这个平台写博客也只是因为一次机缘巧合情况下得知写博客可以获取奖赏&#xff0c;所以那个时期开始疯狂在CSDN发文记录自己编程学习过程&#xff0c;但是至今也未从写作中获利一分哈…...

特征工程-特征预处理

1.7 特征工程-特征预处理 学习目标 目标 了解什么是特征预处理知道归一化和标准化的原理及区别 1 什么是特征预处理 1.1 特征预处理定义 scikit-learn的解释 provides several common utility functions and transformer classes to change raw feature vectors into a represe…...

代码随想录算法训练营day22

代码随想录算法训练营 —day22 文章目录 代码随想录算法训练营前言回溯算法理论基础回溯法解决的问题回溯法模板 一、77. 组合二、216. 组合总和 III三、17. 电话号码的字母组合总结 前言 今天是算法营的第22天&#xff0c;希望自己能够坚持下来&#xff01; 今日任务&#x…...

2024秋语法分析作业-B(满分25分)

特别注意&#xff1a;第17条产生式改为 17) Stmt → while ( Cond ) Stmt 【问题描述】 本次作业只测试一个含简单变量声明、赋值语句、输出语句、if语句和while语句的文法&#xff1a; 0) CompUnit → Block 1) Block → { BlockItemList } 2) BlockItemList → BlockItem…...

Python爬虫入门(1)

在互联网时代&#xff0c;数据成为了最宝贵的资源之一。Python作为一种功能强大的编程语言&#xff0c;因其简洁的语法和丰富的库支持&#xff0c;成为了编写网络爬虫的首选。本文将带你入门Python爬虫技术&#xff0c;让你能够从互联网上自动获取数据。 什么是爬虫&#xff1…...

鸿蒙1.2:第一个应用

1、create Project&#xff0c;选择Empty Activity 2、配置项目 project name 为项目名称&#xff0c;建议使用驼峰型命名 Bundle name 为项目包名 Save location 为保存位置 Module name 为模块名称&#xff0c;即运行时需要选择的模块名称&#xff0c;见下图 查看模块名称&…...

2024年常用工具

作为本年度高频使用工具&#xff0c;手机端也好&#xff0c;桌面端也好&#xff0c;筛选出来9款产品&#xff0c;这里也分享给关注我的小伙伴 &#xff0c;希望对你有些帮助&#xff0c;如果你更好的产品推荐&#xff0c;欢迎留言给我。 即刻 产品经理的聚集地&#xff0c;“让…...

【蓝桥杯】走迷宫

题目&#xff1a; 解题思路&#xff1a; 简单的广度优先算法&#xff08;BFS&#xff09; BFS 的特性 按层次遍历&#xff1a;BFS 按照节点的距离&#xff08;边的数量&#xff09;来逐层访问节点。保证最短路径&#xff1a;对于无权图&#xff08;所有边权重相同&#xff0…...

【pyqt】(三)designer

designer ui设计 在学习后续的代码之前&#xff0c;我们可以先学习一下designer这款工具&#xff0c;在安装软件的时候我们有提到过&#xff0c;其具体位置在虚拟环境根目录下的\Lib\site-packages\PySide6文件夹中。对于新手而言&#xff0c;使用这种可视化的工具可以帮助我们…...

【Go学习】-01-3-函数 结构体 接口 IO

【Go学习】-01-3-函数 结构体 接口 IO 1 函数1.1 函数概述1.1.1 函数做为参数1.1.2 函数返回值 1.2 参数1.3 匿名函数1.4 闭包1.5 延迟调用1.6 异常处理 2 结构体2.1 实例化2.2 匿名结构体2.3 匿名字段 3 类方法3.1 接收器3.2 类方法练习&#xff1a;二维矢量模拟玩家移动3.3 给…...

昆仑万维大数据面试题及参考答案

请介绍一下 Flume 组件。 Flume 是一个分布式、可靠、高可用的海量日志采集、聚合和传输的系统。 从架构层面来看,它主要包含以下几个关键部分。首先是 Source,它是数据的收集端,能够接收多种不同来源的数据。比如,它可以从各种服务器的日志文件中读取数据,像 Web 服务器产…...

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World 2025/1/3 14:06 百度&#xff1a;android studio helloworld android studio hello world kotlin helloword kotlin 串口 no run configurations added android studio no run configurations added 1、…...

Hack The Box-Starting Point系列Three

答案 How many TCP ports are open?&#xff08;靶机开了几个TCP端口&#xff09; 2What is the domain of the email address provided in the “Contact” section of the website?&#xff08;网站的“CONTACT”部分提供的电子邮件地址的域是什么&#xff1f;&#xff09…...

【Python其他生成随机字符串的方法】

在Python中&#xff0c;除了之前提到的方法外&#xff0c;确实还存在其他几种生成随机字符串的途径。以下是对这些方法的详细归纳&#xff1a; 方法一&#xff1a;使用random.randint结合ASCII码生成 你可以利用random.randint函数生成指定范围内的随机整数&#xff0c;这些整…...

redis7基础篇2 redis的主从模式1

目录 一 主从模式 1.1 主从复制的作用 1.2 配置常用命令 1.3 主从复制常见问题 1.4 主从复制的缺点 1.5 redis主从复制原理 二 redis主从复制的搭建流程 2.1 注意事项 2.2 redis的主从复制架构图 2.3 以6379.conf配置文件配置为例 2.4 以6380.conf配置文件配置为例 …...

Springboot - Web

Spring Boot 是一个用于简化 Spring 应用程序配置和部署的框架。它提供了一种快速开发的方式&#xff0c;通过默认配置、自动化配置等特性&#xff0c;使得开发者能够更快捷地构建和部署基于 Spring 的应用。 Spring Boot Web 是 Spring Boot 的一个子模块&#xff0c;它专注于…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

Spring Boot + MyBatis 集成支付宝支付流程

Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例&#xff08;电脑网站支付&#xff09; 1. 添加依赖 <!…...